diff --git a/common.props b/common.props index eb7060b4ec..4eb29c0b8e 100644 --- a/common.props +++ b/common.props @@ -1,7 +1,7 @@ latest - 5.0.0 + 5.1.0 $(NoWarn);CS1591;CS0436 https://abp.io/assets/abp_nupkg.png https://abp.io/ diff --git a/docs/en/Tutorials/Part-5.md b/docs/en/Tutorials/Part-5.md index 4a4611809a..9c2863ca0a 100644 --- a/docs/en/Tutorials/Part-5.md +++ b/docs/en/Tutorials/Part-5.md @@ -10,7 +10,7 @@ In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies: -* **{{DB_Value}}** as the ORM provider. +* **{{DB_Value}}** as the ORM provider. * **{{UI_Value}}** as the UI Framework. This tutorial is organized as the following parts; @@ -142,7 +142,7 @@ Now, you can use the permissions to authorize the book management. ### Application Layer & HTTP API -Open the `BookAppService` class and add set the policy names as the permission names defined above: +Open the `BookAppService` class and set the policy names as the permission names defined above: ````csharp using System; diff --git a/docs/zh-Hans/Modules/Identity.md b/docs/zh-Hans/Modules/Identity.md index 420b696a7c..73f05af7bb 100644 --- a/docs/zh-Hans/Modules/Identity.md +++ b/docs/zh-Hans/Modules/Identity.md @@ -1,28 +1,320 @@ # 身份管理模块 -身份模块基于Microsoft Identity库用于管理[组织单元](Organization-Units.md), 角色, 用户和他们的权限. +身份模块基于 [Microsoft Identity 库](https://docs.microsoft.com/zh-cn/aspnet/core/security/authentication/identity) 用于管理角色,用户及其权限. -> 参阅 [源码](https://github.com/abpframework/abp/tree/dev/modules/identity). 文档很快会被完善. +## 如何安装 -## Identity安全日志 +当你使用 ABP 框架 [创建一个新的解决方案](https://abp.io/get-started) 时,此模块将被预安装(作为 NuGet/NPM 包)。你可以继续用其作为包并轻松地获取更新,也可以将其源代码包含在解决方案中(请参阅 `get-source` [CLI](../CLI.md))以开发自定义模块。 -安全日志可以记录账户的一些重要的操作或者改动, 你可以在在一些功能中保存安全日志. +### 源代码 -你可以注入和使用 `IdentitySecurityLogManager` 或 `ISecurityLogManager` 来保存安全日志. 默认它会创建一个安全日志对象并填充常用的值. 如 `CreationTime`, `ClientIpAddress`, `BrowserInfo`, `current user/tenant`等等. 当然你可以自定义这些值. +可以 [在此处](https://github.com/abpframework/abp/tree/dev/modules/identity) 访问源代码。源代码使用 [MIT](https://choosealicense.com/licenses/mit/) 许可, 所以你可以免费使用和自定义它. + +## 用户界面 + +此模块提供了 [Blazor](../UI/Blazor/Overall.md), [Angular](../UI/Angular/Quick-Start.md) 和 [MVC / Razor Pages](../UI/AspNetCore/Overall.md) 的 UI 可选. + +### 菜单项 + +此模块在 *管理* 菜单下添加了一个 *身份管理* 菜单项: + +![身份模块菜单](../images/identity-module-menu.png) + +这个菜单项和相关页面已获得授权. 这意味着当前用户必须拥有相关权限才能使其可见. `admin` 角色 (和拥有此角色的用户, 如 `admin` 用户) 已经拥有这些权限. 如果你想要使其他角色/用户也启用权限, 请打开 *角色* 或 *用户* 页面的 *权限* 对话框, 并检查如下所示的权限: + +![身分模块权限](../images/identity-module-permissions.png) + +请参阅 [授权文档](../Authorization.md) 以了解权限系统. + +### 页面 + +本节介绍此模块提供的主要页面. + +#### 用户 + +此页用于查看用户列表. 您可以创建/编辑和删除用户, 将角色分配给用户. + +![身分模块用户](../images/identity-module-users.png) + +一个用户可以有零个或多个角色. 用户从其角色继承权限. 此外, 你可以给用户直接分配权限 (通过点击 *操作* 按钮, 然后选择 *权限*) . + +#### 角色 + +角色用于按分组给用户分配权限. + +![身份模块角色](../images/identity-module-roles.png) + +角色除了角色名称之外还有2个属性: + +* `Default`: 如果某个角色被标记为 "default", 那么当新用户 (使用 [账户模块](Account.md)) 注册到应用程序时, 其将被作为默认角色分配给新用户. +* `Public`: 用户的公共角色可以被应用程序中的其他用户看到. 这个功能在身份模块中没有用到, 但被提供作为你可能希望在你自己的应用程序中使用的功能. + +## 其他功能 + +本节包括此模块提供的没有 UI 页面的其他功能. + +### 组织单元 + +组织单元 (OU) 可被用于 **按层级对用户和实体进行分组**. + +#### 组织单元实体 + +组织单元由 **OrganizationUnit** 实体表示. 它的基本属性是: + +- **TenantId**: 组织单元的租户Id. 对于宿主可以是 null. +- **ParentId**: 父级组织单元的Id. 如果这是一个根级组织单元,它可以是 null. +- **Code**: 对于租户唯一的层级字符串编码. +- **DisplayName**: 组织单元的显示名称. + +#### 组织树 + +由于组织单元可以有父级, 因此租户的所有组织单元是一个 **树** 结构. 这个树有一些规则; + +- 可以有多个根级 (`ParentId` 是 `null` 的) . +- 一个组织单元的第一级子项数量有限制 (因为下面解释的固定的组织单元编码单位长度) . + +#### 组织单元编码 + +组织单元编码是通过 `OrganizationUnitManager` 服务自动生成和维护的. 它是一个字符串, 像这样: + +"**00001.00042.00005**" + +通常用这种编码可以轻易地 (递归) 查询出数据库中组织单元的所有子项. 这个编码有一些规则 (当你使用 `OrganizationUnitManager` 时自动被应用的) : + +- 对 [租户](../Multi-Tenancy.md) 是 **唯一的**. +- 同一组织单元的全部子项都具有 **以父级组织单元编码开头的** 编码. +- 如示例中所示, 它是固定长度的且是基于树中组织单元的级别的. +- 尽管组织单元编码是唯一的, 但如果你移动了相关的组织单元, 它也可以被更改. + +请注意, 你必须根据Id引用一个组织单元, 而不是编码, 因为编码后续是可以被更改的. + +#### 组织单元管理 + +`OrganizationUnitManager` 类可以被 [注入](../Dependency-Injection.md) 并用来管理组织单元. 常见用例如下: + +- 创建, 更新和删除组织单元. +- 在组织单元树中移动一个组织单元. +- 获取关于组织单元树及其项的信息. + +### 身份安全日志 + +安全日志系统可以记录账户的一些重要的操作或者改动 (例如 *登录* 和 *更改密码*) . 如果需要, 你也可以保存安全日志. + +你可以注入和使用 `IdentitySecurityLogManager` 或 `ISecurityLogManager` 来写入安全日志. 默认它会创建一个日志对象并填充常用的值, 如 `CreationTime`, `ClientIpAddress`, `BrowserInfo`, `current user/tenant` 等等. 当然你可以覆盖这些值. ```cs await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() { - Identity = "IdentityServer"; - Action = "ChangePassword"; + Identity = "IdentityServer"; + Action = "ChangePassword"; }); ``` -通过配置 `AbpSecurityLogOptions` 来提供应用程序的名称或者禁用安全日志功能. 默认是**启用**状态. +通过配置 `AbpSecurityLogOptions` 来为日志提供应用程序的名称 (如果你有多个应用程序并且想要在日志中区分应用程序) 或者禁用安全日志功能. ```cs Configure(options => { - options.ApplicationName = "AbpSecurityTest"; + options.ApplicationName = "AbpSecurityTest"; }); ``` +## 选项 + +`IdentityOptions` 是由 Microsoft [Identity 库](https://docs.microsoft.com/zh-cn/aspnet/core/security/authentication/identity) 提供的标准 [选项类](../Options.md) . 所以, 你可以在 [模块](../Module-Development-Basics.md) 类的 `ConfigureServices` 方法中设置这些选项. + +**例如: 设置所需的密码最小长度** + +````csharp +Configure(options => +{ + options.Password.RequiredLength = 5; +}); +```` + +ABP 允许你在运行时通过使用 [设置系统](../Settings.md) 更进一步地更改这些选项. 你可以 [注入](../Dependency-Injection.md) `ISettingManager` 并使用 `Set...` 方法中的一种来更改用户, 租户或全局所有用户的选项值. + +**例如: 更改当前租户所需的密码最小长度** + +````csharp +public class MyService : ITransientDependency +{ + private readonly ISettingManager _settingManager; + + public MyService(ISettingManager settingManager) + { + _settingManager = settingManager; + } + + public async Task ChangeMinPasswordLength(int minLength) + { + await _settingManager.SetForCurrentTenantAsync( + IdentitySettingNames.Password.RequiredLength, + minLength.ToString() + ); + } +} +```` + +`IdentitySettingNames` 类 (在 `Volo.Abp.Identity.Settings` 命名空间中) 为设置名称定义了一些常量. + +## 分布式事件 + +此模块定义了如下 ETOs (事件传输对象) 以允许你去订阅模块中实体的改动. + +* `UserEto` 在 `IdentityUser` 实体更改完成时发布. +* `IdentityRoleEto` 在 `IdentityRole` 实体更改完成时发布. +* `IdentityClaimTypeEto` 在 `IdentityClaimType` 实体更改完成时发布. +* `OrganizationUnitEto` 在 `OrganizationUnit` 实体更改完成时发布. + +**例如: 当一个新用户被创建时接收通知** + +````csharp +public class MyHandler : + IDistributedEventHandler>, + ITransientDependency +{ + public async Task HandleEventAsync(EntityCreatedEto eventData) + { + UserEto user = eventData.Entity; + // TODO: ... + } +} +```` + +`UserEto` 和 `IdentityRoleEto` 默认自动发布事件. 你应该自己配置其他的 Eto. 请参阅 [分布式事件总线文档](../Distributed-Event-Bus.md) 了解预定义事件的详细信息. + +> 订阅分布式事件对于分布式场景 (如微服务架构) 特别有用. 如果你正在构建单体式应用程序, 或者在运行了身份模块的同一进程内监听事件, 那么订阅 [本地事件](../Local-Event-Bus.md) 更高效, 更简单. + +## 内部构件 + +本节包括模块的一些内部详细信息, 您不是那么需要, 但在一些情况下可能需要使用. + +### 领域层 + +#### 聚合 + +##### 用户 + +用户通常是一个登录并使用应用程序的人. + +* `IdentityUser` (聚合根): 表示系统中的一个用户. + * `IdentityUserRole` (集合): 分配给用户的角色. + * `IdentityUserClaim` (集合): 用户的自定义声明. + * `IdentityUserLogin` (集合): 用户的外部登录. + * `IdentityUserToken` (集合): 用户的令牌 (用于 Microsoft Identity 服务). + +##### 角色 + +角色通常是分配给用户的一组权限. + +* `IdentityRole` (聚合根): 表示系统中的角色. + * `IdentityRoleClaim` (集合): 角色的自定义声明. + +##### 声明类型 + +声明类型是可以被分配给系统中其他实体 (如角色和用户) 的自定义声明的定义. + +* `IdentityClaimType` (聚合根): 表示声明类型的定义. 它包括一些用于定义声明类型和验证规则的属性 (例如, 必须的, 正则表达式, 描述, 值类型) . + +##### 身份安全日志 + +`IdentitySecurityLog` 对象表示系统中与授权相关的操作 (如 *登录*) . + +* `IdentitySecurityLog` (聚合根): 表示系统中的安全日志. + +##### 组织单元 + +组织单元是一个有层级结构的实体. + +* ```OrganizationUnit``` (聚合根): 表示系统中的组织单元. + * ```Roles``` (集合): 组织单元的角色. + +#### 仓储 + +此模块定义了以下自定义仓储: + +* `IIdentityUserRepository` +* `IIdentityRoleRepository` +* `IIdentityClaimTypeRepository` +* ```IIdentitySecurityLogRepository``` +* ```IOrganizationUnitRepository``` + +#### 领域服务 + +##### 用户管理 + +`IdentityUserManager` 常用于管理用户, 他们的权限, 声明, 密码, 电子邮件等等. 它派生自 Microsoft Identity 的 `UserManager` 类, 其中 `T` 是 `IdentityUser`. + +##### 角色管理 + +`IdentityRoleManager` 常用于管理角色和他们的声明. 它派生自 Microsoft Identity 的 `RoleManager` 类, 其中 `T` 是 `IdentityRole`. + +##### 声明类型管理 + +`IdenityClaimTypeManager` 常用于对聚合根 `IdentityClaimType` 执行某些操作. + +##### 组织单元管理 + +```OrganizationUnitManager``` 常用于对聚合根 `OrganizationUnit` 执行某些操作. + +##### 安全日志管理 + +```IdentitySecurityLogManager``` 常用于保存安全日志. + +### 服务层 + +#### 应用服务 + +* `IdentityUserAppService` (实现 `IIdentityUserAppService`): 实现了用户管理 UI 的用例. +* `IdentityRoleAppService` (实现 `IIdentityRoleAppService`): 实现了角色管理 UI 的用例. +* `IdentityClaimTypeAppService` (实现 `IIdentityClaimTypeAppService`): 实现了声明类型管理 UI 的用例. +* `IdentitySettingsAppService` (实现 `IIdentitySettingsAppService`): 用于获取和更新身份模块的设置. +* `IdentityUserLookupAppService` (实现 `IIdentityUserLookupAppService`): 用于根据 `id` 或 `userName` 获取用户信息. 它旨在由ABP内部使用. +* `ProfileAppService` (实现 `IProfileAppService`): 用于更改用户的简介和密码. +* ```IdentitySecurityLogAppService``` (实现 ```IIdentitySecurityLogAppService```): 实现了安全日志 UI 的用例. +* ```OrganizationUnitAppService``` (实现 ```OrganizationUnitAppService```): 实现了组织单元管理 UI 的用例. + +### 数据库提供程序 + +此模块为数据库提供 [Entity Framework Core](../Entity-Framework-Core.md) 和 [MongoDB](../MongoDB.md) 两种选择. + +#### EF Core + +NuGet 包 [Volo.Abp.Identity.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Abp.Identity.EntityFrameworkCore) 实现了 EF Core 的集成. + +##### 数据库表 + +* **AbpRoles** + * AbpRoleClaims +* **AbpUsers** + * AbpUserClaims + * AbpUserLogins + * AbpUserRoles + * AbpUserTokens +* **AbpClaimTypes** +* **AbpOrganizationUnits** + * AbpOrganizationUnitRoles + * AbpUserOrganizationUnits +* **AbpSecurityLogs** + +#### MongoDB + +NuGet 包 [Volo.Abp.Identity.MongoDB](https://www.nuget.org/packages/Volo.Abp.Identity.MongoDB) 实现了 MongoDB 的集成. + +##### 数据库集合 + +* **AbpRoles** +* **AbpUsers** +* **AbpClaimTypes** +* **AbpOrganizationUnits** +* **AbpSecurityLogs** + +#### 常用数据库属性 + +你可以设置 `AbpIdentityDbProperties` 中的以下属性来更改数据库选项: + +* `DbTablePrefix` (`Abp` 作为默认值) 是表/集合名称的前缀. +* `DbSchema` (`null` 作为默认值) 是数据库架构. +* `ConnectionStringName` (`AbpIdentity` 作为默认值) 是此模块的 [连接字符串](../Connection-Strings.md) 名称. + +它们是静态属性. 你需要在开始运行应用程序前设置它们 (通常在 `Program.cs` 中). diff --git a/docs/zh-Hans/Modules/IdentityServer.md b/docs/zh-Hans/Modules/IdentityServer.md index 838cdfe3ed..1359c04e53 100644 --- a/docs/zh-Hans/Modules/IdentityServer.md +++ b/docs/zh-Hans/Modules/IdentityServer.md @@ -1,18 +1,18 @@ # 身份服务器模块 -身份服务器模块提供了一个 [IdentityServer](https://github.com/IdentityServer/IdentityServer4) (IDS) 的完全集成,该框架提供高级身份验证功能,如单点登录和API访问控制.此模块将客户端,资源以及其他 IDS 相关的对象保存到数据库中. +身份服务器模块提供了一个 [IdentityServer](https://github.com/IdentityServer/IdentityServer4) (IDS) 的完全集成, 该框架提供高级身份验证功能, 如单点登录和API访问控制.此模块将客户端,资源以及其他 IDS 相关的对象保存到数据库中. ## 如何安装 -当你使用 ABP 框架 [创建一个新的解决方案](https://abp.io/get-started) 时,此模块将被预安装(作为 NuGet/NPM 包).你可以继续用其作为包并轻松地获取更新,也可以将其源代码包含在解决方案中(请参阅 `get-source` [CLI](../CLI.md))以开发自定义模块. +当你使用 ABP 框架 [创建一个新的解决方案](https://abp.io/get-started) 时, 此模块将被预安装(作为 NuGet/NPM 包).你可以继续用其作为包并轻松地获取更新, 也可以将其源代码包含在解决方案中(请参阅 `get-source` [CLI](../CLI.md))以开发自定义模块. ### 源代码 -可以 [在此处](https://github.com/abpframework/abp/tree/dev/modules/identityserver) 访问源代码.源代码使用 [MIT](https://choosealicense.com/licenses/mit/) 许可,所以你可以免费使用和自定义它. +可以 [在此处](https://github.com/abpframework/abp/tree/dev/modules/identityserver) 访问源代码.源代码使用 [MIT](https://choosealicense.com/licenses/mit/) 许可, 所以你可以免费使用和自定义它. ## 用户界面 -此模块使用了领域逻辑和数据库集成,但没有提供任何 UI.如果你需要动态添加客户端和资源, 管理 UI 是非常有用的.在这种情况下,你可以自己构建管理 UI,或者考虑购买为此模块提供了管理 UI 的 [ABP 商业版](https://commercial.abp.io/). +此模块使用了领域逻辑和数据库集成, 但没有提供任何 UI.如果你需要动态添加客户端和资源, 管理 UI 是非常有用的.在这种情况下, 你可以自己构建管理 UI, 或者考虑购买为此模块提供了管理 UI 的 [ABP 商业版](https://commercial.abp.io/). ## 与其他模块的关系 @@ -22,15 +22,15 @@ ### AbpIdentityServerBuilderOptions -`AbpIdentityServerBuilderOptions` 在你的身份服务器 [模块](https://docs.abp.io/en/abp/latest/Module-Development-Basics) 中的 `PreConfigureServices` 方法中配置.例如: +`AbpIdentityServerBuilderOptions` 在你的身份服务器 [模块](https://docs.abp.io/zh-Hans/abp/latest/Module-Development-Basics) 中的 `PreConfigureServices` 方法中配置.例如: ````csharp public override void PreConfigureServices(ServiceConfigurationContext context) { - PreConfigure(builder => - { - //Set options here... - }); + PreConfigure(builder => + { + //Set options here... + }); } ```` @@ -41,15 +41,15 @@ public override void PreConfigureServices(ServiceConfigurationContext context) * `IntegrateToAspNetIdentity` (默认值:true):集成到 ASP.NET Identity. * `AddDeveloperSigningCredential` (默认值:true):设置为 false 禁止调用 IIdentityServerBuilder 中的 `AddDeveloperSigningCredential()`. -`IIdentityServerBuilder` 可以在你的身份服务器 [模块](https://docs.abp.io/en/abp/latest/Module-Development-Basics) 中的 `PreConfigureServices` 方法中配置.例如: +`IIdentityServerBuilder` 可以在你的身份服务器 [模块](https://docs.abp.io/zh-Hans/abp/latest/Module-Development-Basics) 中的 `PreConfigureServices` 方法中配置.例如: ````csharp public override void PreConfigureServices(ServiceConfigurationContext context) { - PreConfigure(builder => - { - builder.AddSigningCredential(...); - }); + PreConfigure(builder => + { + builder.AddSigningCredential(...); + }); } ```` @@ -107,7 +107,7 @@ public override void PreConfigureServices(ServiceConfigurationContext context) #### 领域服务 -此模块不包含任何领域服务,但重写了下面的服务; +此模块不包含任何领域服务, 但重写了下面的服务; * `AbpProfileService` (当 `AbpIdentityServerBuilderOptions.IntegrateToAspNetIdentity` 为 true 时使用) * `AbpClaimsService` @@ -132,13 +132,13 @@ public override void PreConfigureServices(ServiceConfigurationContext context) ##### 表/集合 前缀 & 架构 -所有表/集合都使用 `IdentityServer` 作为默认前缀.如果你需要改变表的前缀或设置一个架构名称(如果你的数据库提供程序支持),请设置 `AbpIdentityServerDbProperties` 类的静态属性. +所有表/集合都使用 `IdentityServer` 作为默认前缀.如果你需要改变表的前缀或设置一个架构名称(如果你的数据库提供程序支持), 请设置 `AbpIdentityServerDbProperties` 类的静态属性. ##### 连接字符串 -此模块使用 `AbpIdentityServer` 作为连接字符串的名称.如果你没有用这个名称定义连接字符串,它将回退到 `Default` 连接字符串. +此模块使用 `AbpIdentityServer` 作为连接字符串的名称.如果你没有用这个名称定义连接字符串, 它将回退到 `Default` 连接字符串. -有关详细信息,请参阅 [连接字符串](https://docs.abp.io/en/abp/latest/Connection-Strings) 文档. +有关详细信息, 请参阅 [连接字符串](https://docs.abp.io/zh-Hans/abp/latest/Connection-Strings) 文档. #### EF Core diff --git a/docs/zh-Hans/Tutorials/Part-5.md b/docs/zh-Hans/Tutorials/Part-5.md index 602306c484..be687a6cb9 100644 --- a/docs/zh-Hans/Tutorials/Part-5.md +++ b/docs/zh-Hans/Tutorials/Part-5.md @@ -1 +1,635 @@ -TODO.. \ No newline at end of file +# Web应用程序开发教程 - 第五章: 授权 +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer","NG"], + "DB": ["EF","Mongo"] +} +```` +## 关于本教程 + +在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的: + +* **{{DB_Text}}** 做为ORM提供程序. +* **{{UI_Value}}** 做为UI框架. + +本教程分为以下部分: + +- [Part 1: 创建服务端](Part-1.md) +- [Part 2: 图书列表页面](Part-2.md) +- [Part 3: 创建,更新和删除图书](Part-2.md) +- [Part 4: 集成测试](Part-4.md) +- **Part 5: 授权**(本章) +- [Part 6: 作者: 领域层](Part-6.md) +- [Part 7: 作者: 数据库集成](Part-7.md) +- [Part 8: 作者: 应用服务层](Part-8.md) +- [Part 9: 作者: 用户页面](Part-9.md) +- [Part 10: 图书到作者的关系](Part-10.md) + +## 下载源码 + +本教程根据你的**UI** 和 **数据库**偏好有多个版本,我们准备了几种可供下载的源码组合: + +* [MVC (Razor Pages) UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Blazor UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore) +* [Angular UI 与 MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +> 如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 [在Windows 10中启用长路径](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). + +> 如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path +> `git config --system core.longpaths true` + +{{if UI == "MVC" && DB == "EF"}} + +### 视频教程 + +本章也被录制为视频教程 **发布在YouTube**. + +{{end}} + +## 权限 + +ABP框架提供了一个基于ASP.NET Core[授权基础架构](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction)的[授权系统](../Authorization.md). 基于标准授权基础架构的一个主要功能是添加了 **权限系统**, 这个系统允许定义权限并且根据角色, 用户或客户端启用/禁用权限. + +### 权限名称 + +权限必须有唯一的名称 (一个 `字符串`). 最好的方法是把它定义为一个 `常量`, 这样我们就可以重用这个权限名称了. + +打开 `Acme.BookStore.Application.Contracts` 项目中的 `BookStorePermissions` 类 (位于 `Permissions` 文件夹) 并替换为以下代码: + +````csharp +namespace Acme.BookStore.Permissions +{ + public static class BookStorePermissions + { + public const string GroupName = "BookStore"; + + public static class Books + { + public const string Default = GroupName + ".Books"; + public const string Create = Default + ".Create"; + public const string Edit = Default + ".Edit"; + public const string Delete = Default + ".Delete"; + } + } +} +```` + +权限名称具有层次结构. 例如, "创建图书" 权限被定义为 `BookStore.Books.Create`. ABP不强制必须如此, 但这是一种有益的做法. + +### 权限定义 + +在使用权限前必须定义它们. + +打开 `Acme.BookStore.Application.Contracts` 项目中的 `BookStorePermissionDefinitionProvider` 类 (位于 `Permissions` 文件夹) 并替换为以下代码: + +````csharp +using Acme.BookStore.Localization; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Localization; + +namespace Acme.BookStore.Permissions +{ + public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider + { + public override void Define(IPermissionDefinitionContext context) + { + var bookStoreGroup = context.AddGroup(BookStorePermissions.GroupName, L("Permission:BookStore")); + + var booksPermission = bookStoreGroup.AddPermission(BookStorePermissions.Books.Default, L("Permission:Books")); + booksPermission.AddChild(BookStorePermissions.Books.Create, L("Permission:Books.Create")); + booksPermission.AddChild(BookStorePermissions.Books.Edit, L("Permission:Books.Edit")); + booksPermission.AddChild(BookStorePermissions.Books.Delete, L("Permission:Books.Delete")); + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } + } +} +```` + +这个类定义了一个 **权限组** (在UI上分组权限, 下文会看到) 和 权限组中的**4个权限**. 而且, **创建**, **编辑** 和 **删除** 是 `BookStorePermissions.Books.Default` 权限的子权限. **仅当父权限被选择**时, 子权限才能被选择. + +最后, 编辑本地化文件 (`Acme.BookStore.Domain.Shared` 项目的 `Localization/BookStore` 文件夹中的 `en.json`) 定义上面使用的本地化键: + +````json +"Permission:BookStore": "Book Store", +"Permission:Books": "Book Management", +"Permission:Books.Create": "Creating new books", +"Permission:Books.Edit": "Editing the books", +"Permission:Books.Delete": "Deleting the books" +```` + +> 本地化键名可以是任意的, 并没有强制的规则. 但我们推荐上面使用的约定. + +### 权限管理界面 + +完成权限定义后, 可以在**权限管理模态窗口**看到它们. + +在*管理 -> Identity -> 角色* 页面, 选择admin角色的 *权限* 操作, 打开权限管理模态窗口: + +![bookstore-permissions-ui](images/bookstore-permissions-ui.png) + +授予你希望的权限并保存. + +> **提示**: 如果运行 `Acme.BookStore.DbMigrator` 应用程序, 新权限会被自动授予admin. + +## 授权 + +现在, 你可以使用权限授权图书管理. + +### 应用层 和 HTTP API + +打开 the `BookAppService` 类, 设置策略名称为上面定义的权限名称. + +````csharp +using System; +using Acme.BookStore.Permissions; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace Acme.BookStore.Books +{ + public class BookAppService : + CrudAppService< + Book, //The Book entity + BookDto, //Used to show books + Guid, //Primary key of the book entity + PagedAndSortedResultRequestDto, //Used for paging/sorting + CreateUpdateBookDto>, //Used to create/update a book + IBookAppService //implement the IBookAppService + { + public BookAppService(IRepository repository) + : base(repository) + { + GetPolicyName = BookStorePermissions.Books.Default; + GetListPolicyName = BookStorePermissions.Books.Default; + CreatePolicyName = BookStorePermissions.Books.Create; + UpdatePolicyName = BookStorePermissions.Books.Edit; + DeletePolicyName = BookStorePermissions.Books.Delete; + } + } +} +```` + +加入代码到构造器. 基类中的 `CrudAppService` 自动在CRUD操作中使用这些权限. 这不仅实现了 **应用服务** 的安全性, 也实现了 **HTTP API** 安全性, 因为如前解释的, HTTP API 自动使用这些服务. (参阅 [自动 API controllers](../API/Auto-API-Controllers.md)). + +> 在稍后开发作者管理功能时, 你将会看到声明式授权, 使用 `[Authorize(...)]` 特性. + +{{if UI == "MVC"}} + +### Razor 页面 + +虽然安全的 HTTP API和应用服务阻止未授权用户使用服务, 但他们依然可以导航到图书管理页面. 虽然当页面发起第一个访问服务器的AJAX请求时会收到授权异常, 但为了更好的用户体验和安全性, 我们应该对页面进行授权. + +打开 `BookStoreWebModule` 在 `ConfigureServices` 方法中加入以下代码: + +````csharp +Configure(options => +{ + options.Conventions.AuthorizePage("/Books/Index", BookStorePermissions.Books.Default); + options.Conventions.AuthorizePage("/Books/CreateModal", BookStorePermissions.Books.Create); + options.Conventions.AuthorizePage("/Books/EditModal", BookStorePermissions.Books.Edit); +}); +```` + +现在未授权用户会被重定向至**登录页面**. + +#### 隐藏新建图书按钮 + +图书管理页面有一个 *新建图书* 按钮, 当用户没有 *图书新建* 权限时就不可见的. + +![bookstore-new-book-button-small](images/bookstore-new-book-button-small.png) + +打开 the `Pages/Books/Index.cshtml` 文件, 替换内容为以下代码: + +````html +@page +@using Acme.BookStore.Localization +@using Acme.BookStore.Permissions +@using Acme.BookStore.Web.Pages.Books +@using Microsoft.AspNetCore.Authorization +@using Microsoft.Extensions.Localization +@model IndexModel +@inject IStringLocalizer L +@inject IAuthorizationService AuthorizationService +@section scripts +{ + +} + + + + + + @L["Books"] + + + @if (await AuthorizationService.IsGrantedAsync(BookStorePermissions.Books.Create)) + { + + } + + + + + + + +```` + +* 加入 `@inject IAuthorizationService AuthorizationService` 以访问授权服务. +* 使用 `@if (await AuthorizationService.IsGrantedAsync(BookStorePermissions.Books.Create))` 检查图书创建权限, 条件显示 *新建图书* 按钮. + +### JavaScript端 + +图书管理页面中的图书表格每行都有操作按钮. 操作按钮包括 *编辑* 和 *删除* 操作: + +![bookstore-edit-delete-actions](images/bookstore-edit-delete-actions.png) + +如果用户没有权限, 应该隐藏相关的操作. 表格行中的操作有一个 `visible` 属性, 可以设置为 `false` 隐藏操作项. + +打开 `Acme.BookStore.Web` 项目中的 `Pages/Books/Index.js`, 为 `编辑` 操作加入 `visible` 属性: + +````js +{ + text: l('Edit'), + visible: abp.auth.isGranted('BookStore.Books.Edit'), //CHECK for the PERMISSION + action: function (data) { + editModal.open({ id: data.record.id }); + } +} +```` + +对 `Delete` 操作进行同样的操作: + +````js +visible: abp.auth.isGranted('BookStore.Books.Delete') +```` + +* `abp.auth.isGranted(...)` 检查前面定义的权限. +* `visible` 也可以是一个返回 `bool` 值的函数. 这个函数可以稍后根据某些条件计算. + +### 菜单项 + +即使我们在图书管理页面的所有层都控制了权限, 应用程序的主菜单依然会显示. 我们应该隐藏用户没有权限的菜单项. + +打开 `BookStoreMenuContributor` 类, 找到下面的代码: + +````csharp +context.Menu.AddItem( + new ApplicationMenuItem( + "BooksStore", + l["Menu:BookStore"], + icon: "fa fa-book" + ).AddItem( + new ApplicationMenuItem( + "BooksStore.Books", + l["Menu:Books"], + url: "/Books" + ) + ) +); +```` + +替换为以下代码: + +````csharp +var bookStoreMenu = new ApplicationMenuItem( + "BooksStore", + l["Menu:BookStore"], + icon: "fa fa-book" +); + +context.Menu.AddItem(bookStoreMenu); + +//CHECK the PERMISSION +if (await context.IsGrantedAsync(BookStorePermissions.Books.Default)) +{ + bookStoreMenu.AddItem(new ApplicationMenuItem( + "BooksStore.Books", + l["Menu:Books"], + url: "/Books" + )); +} +```` + +你需要为 `ConfigureMenuAsync` 方法加入 `async` 关键字, 并重新组织返回值. 最终的 `BookStoreMenuContributor` 类应该如下: + +````csharp +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using Acme.BookStore.Localization; +using Acme.BookStore.MultiTenancy; +using Acme.BookStore.Permissions; +using Volo.Abp.TenantManagement.Web.Navigation; +using Volo.Abp.UI.Navigation; + +namespace Acme.BookStore.Web.Menus +{ + public class BookStoreMenuContributor : IMenuContributor + { + public async Task ConfigureMenuAsync(MenuConfigurationContext context) + { + if (context.Menu.Name == StandardMenus.Main) + { + await ConfigureMainMenuAsync(context); + } + } + + private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) + { + if (!MultiTenancyConsts.IsEnabled) + { + var administration = context.Menu.GetAdministration(); + administration.TryRemoveMenuItem(TenantManagementMenuNames.GroupName); + } + + var l = context.GetLocalizer(); + + context.Menu.Items.Insert(0, new ApplicationMenuItem("BookStore.Home", l["Menu:Home"], "~/")); + + var bookStoreMenu = new ApplicationMenuItem( + "BooksStore", + l["Menu:BookStore"], + icon: "fa fa-book" + ); + + context.Menu.AddItem(bookStoreMenu); + + //CHECK the PERMISSION + if (await context.IsGrantedAsync(BookStorePermissions.Books.Default)) + { + bookStoreMenu.AddItem(new ApplicationMenuItem( + "BooksStore.Books", + l["Menu:Books"], + url: "/Books" + )); + } + } + } +} +```` + +{{else if UI == "NG"}} + +### Angular Guard 配置 + +UI的第一步是防止未认证用户看见"图书"菜单项并进入图书管理页面. + +打开 `/src/app/book/book-routing.module.ts` 替换为以下代码: + +````js +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { AuthGuard, PermissionGuard } from '@abp/ng.core'; +import { BookComponent } from './book.component'; + +const routes: Routes = [ + { path: '', component: BookComponent, canActivate: [AuthGuard, PermissionGuard] }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class BookRoutingModule {} +```` + +* 从 `@abp/ng.core` 引入 `AuthGuard` 和 `PermissionGuard`. +* 在路由定义中添加 `canActivate: [AuthGuard, PermissionGuard]`. + +打开 `/src/app/route.provider.ts`, 在 `/books` 路由中添加 `requiredPolicy: 'BookStore.Books'`. `/books` 路由应该如以下配置: + +````js +{ + path: '/books', + name: '::Menu:Books', + parentName: '::Menu:BookStore', + layout: eLayoutType.application, + requiredPolicy: 'BookStore.Books', +} +```` + +### 隐藏新建图书按钮 + +当用户没有 *图书新建* 权限时, 图书管理页面上的 *新建图书* 按钮应该不可见. + +![bookstore-new-book-button-small](images/bookstore-new-book-button-small.png) + +打开 `/src/app/book/book.component.html` 文件, 替换创建按钮的HTML内容如下: + +````html + + +```` + +* 加入 `*abpPermission="'BookStore.Books.Create'"`, 当用户没有权限时隐藏按钮. + +### 隐藏编辑和删除操作 + +图书管理页面中的图书表格每行都有操作按钮. 操作按钮包括 *编辑* 和 *删除* 操作: + +![bookstore-edit-delete-actions](images/bookstore-edit-delete-actions.png) + +如果用户没有权限, 应该隐藏相关的操作. + +打开 `/src/app/book/book.component.html` 文件, 替换编辑和删除按钮的内容如下: + +````html + + + + + +```` + +* 加入 `*abpPermission="'BookStore.Books.Create'"`, 当用户没有编辑权限时隐藏按钮. +* 加入 `*abpPermission="'BookStore.Books.Delete'"`, 当用户没有删除权限时隐藏按钮. + +{{else if UI == "Blazor"}} + +### Razor验证组件 + +打开 `Acme.BookStore.Blazor` 项目中的 `/Pages/Books.razor` 文件, 在` @page` 指令和命名空间引入(`@using` 行)后添加 `Authorize` 特性, 如下所示: + +````html +@page "/books" +@attribute [Authorize(BookStorePermissions.Books.Default)] +@using Acme.BookStore.Permissions +@using Microsoft.AspNetCore.Authorization +... +```` + +添加这个特性阻止未登录用户或未授权用户访问这个页面. 用户重试后, 会被重定向到登录页面. + +### 显示/隐藏操作 + +图书管理页面上的每一种图书都有 *新建* 按钮和 *编辑*, *删除* 操作. 如果用户没有相关权限, 这些按钮/操作应该被隐藏. + +基类 `AbpCrudPageBase` 已经具有这些操作需要的功能. + +#### 设置策略 (权限) 名称 + +加入以下代码到 `Books.razor` 文件结尾: + +````csharp +@code +{ + public Books() // Constructor + { + CreatePolicyName = BookStorePermissions.Books.Create; + UpdatePolicyName = BookStorePermissions.Books.Edit; + DeletePolicyName = BookStorePermissions.Books.Delete; + } +} +```` + +基类 `AbpCrudPageBase` 自动检查相关操作的权限. 如果需要手动检查, 它也定义了相应的属性. + +* `HasCreatePermission`: True, 如果用户具有新建实体的权限. +* `HasUpdatePermission`: True, 如果用户具有编辑/更新实体的权限. +* `HasDeletePermission`: True, 如果用户具有删除实体的权限. + +> **Blazor 提示**: 当添加少量代码到 `@code` 是没有问题的. 当添加的代码变长时, 建议使用代码后置方法以便于维护. 我们将在作者部分使用这个方法. + +#### 隐藏新建图书按钮 + +检查 *新建图书* 按钮权限: + +````xml +@if (HasCreatePermission) +{ + +} +```` + +#### 隐藏编辑/删除操作 + +`EntityAction` 组件定义了 `Visible` 属性 (参数) 以条件显示操作. + +更新 `EntityActions` 部分: + +````xml + + + + +```` + +#### 关于权限缓存 + +你可以运行和测试权限. 从admin角色中移除一个图书相关权限, 观察到相关按钮/操作从UI上消失. + +在客户端, **ABP框架缓存当前用户的权限** . 所以, 当你修改了你的权限, 你需要手工 **刷新页面**. 如果不刷新并试图使用被禁的操作, 你会从服务器收到一个HTTP 403 (forbidden) 响应. + +> 修改角色或用户的权限在服务端立即生效. 所以, 缓存系统不会导致安全问题. + +### 菜单项 + +即使我们在图书管理页面的所有层都控制了权限, 应用程序的主菜单依然会显示. 我们应该隐藏用户没有权限的菜单项. + +打开 `Acme.BookStore.Blazor` 项目中的 `BookStoreMenuContributor` 类, 找到以下代码: + +````csharp +context.Menu.AddItem( + new ApplicationMenuItem( + "BooksStore", + l["Menu:BookStore"], + icon: "fa fa-book" + ).AddItem( + new ApplicationMenuItem( + "BooksStore.Books", + l["Menu:Books"], + url: "/books" + ) + ) +); +```` + +替换为以下代码: + +````csharp +var bookStoreMenu = new ApplicationMenuItem( + "BooksStore", + l["Menu:BookStore"], + icon: "fa fa-book" +); + +context.Menu.AddItem(bookStoreMenu); + +//CHECK the PERMISSION +if (await context.IsGrantedAsync(BookStorePermissions.Books.Default)) +{ + bookStoreMenu.AddItem(new ApplicationMenuItem( + "BooksStore.Books", + l["Menu:Books"], + url: "/books" + )); +} +```` + +你需要为 `ConfigureMenuAsync` 方法加入 `async` 关键字并重新整理返回值. 最终的 `ConfigureMainMenuAsync` 方法如下: + +````csharp +private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) +{ + var l = context.GetLocalizer(); + + context.Menu.Items.Insert( + 0, + new ApplicationMenuItem( + "BookStore.Home", + l["Menu:Home"], + "/", + icon: "fas fa-home" + ) + ); + + var bookStoreMenu = new ApplicationMenuItem( + "BooksStore", + l["Menu:BookStore"], + icon: "fa fa-book" + ); + + context.Menu.AddItem(bookStoreMenu); + + //CHECK the PERMISSION + if (await context.IsGrantedAsync(BookStorePermissions.Books.Default)) + { + bookStoreMenu.AddItem(new ApplicationMenuItem( + "BooksStore.Books", + l["Menu:Books"], + url: "/books" + )); + } +} +```` + +{{end}} + +## 下一章 + +查看本教程的[下一章](Part-6.md). diff --git a/docs/zh-Hans/Tutorials/Part-6.md b/docs/zh-Hans/Tutorials/Part-6.md index 602306c484..6030c8f3b9 100644 --- a/docs/zh-Hans/Tutorials/Part-6.md +++ b/docs/zh-Hans/Tutorials/Part-6.md @@ -1 +1,278 @@ -TODO.. \ No newline at end of file +# Web应用程序开发教程 - 第六章: 作者: 领域层 +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer","NG"], + "DB": ["EF","Mongo"] +} +```` +## 关于本教程 + +在本系列教程中, 你将构建一个名为 `Acme.BookStore` 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的: + +* **{{DB_Text}}** 做为ORM提供程序. +* **{{UI_Value}}** 做为UI框架. + +本教程分为以下部分: + +- [Part 1: 创建服务端](Part-1.md) +- [Part 2: 图书列表页面](Part-2.md) +- [Part 3: 创建,更新和删除图书](Part-2.md) +- [Part 4: 集成测试](Part-4.md) +- [Part 5: 授权](Part-5.md) +- **Part 6: 作者: 领域层**(本章) +- [Part 7: 作者: 数据库集成](Part-7.md) +- [Part 8: 作者: 应用服务层](Part-8.md) +- [Part 9: 作者: 用户页面](Part-9.md) +- [Part 10: 图书到作者的关系](Part-10.md) + +## 下载源码 + +本教程根据你的**UI** 和 **数据库**偏好有多个版本,我们准备了几种可供下载的源码组合: + +* [MVC (Razor Pages) UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore) +* [Blazor UI 与 EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore) +* [Angular UI 与 MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +> 如果你在Windows中遇到 "文件名太长" or "解压错误", 很可能与Windows最大文件路径限制有关. Windows文件路径的最大长度为250字符. 为了解决这个问题,参阅 [在Windows 10中启用长路径](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later). + +> 如果你遇到与Git相关的长路径错误, 尝试使用下面的命令在Windows中启用长路径. 参阅 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path +> `git config --system core.longpaths true` + +## 简介 + +在前面的章节中, 我们使用 ABP 框架轻松地构建了一些服务; + +* 使用 [CrudAppService](../Application-Services.md) 基类, 而不是为标准的增删改查操作手工开发应用服务. +* 使用 [generic repositories](../Repositories.md) 自动完成数据层功能. + +对于 "作者" 部分; + +* 我们将要展示在需要的情况下, 如何 **手工做一些事情**. +* 我们将要实现一些 **领域驱动设计 (DDD) 最佳实践**. + +> **开发将会逐层完成, 一次聚焦一层. 在真实项目中, 你会逐个功能(垂直)开发, 如同前面的教程. 通过这种方式, 你可以体验这两种方式** + +## 作者实体 + +在 `Acme.BookStore.Domain` 项目中创建 `Authors` 文件夹 (命名空间), 在其中加入 `Author` 类: + +````csharp +using System; +using JetBrains.Annotations; +using Volo.Abp; +using Volo.Abp.Domain.Entities.Auditing; + +namespace Acme.BookStore.Authors +{ + public class Author : FullAuditedAggregateRoot + { + public string Name { get; private set; } + public DateTime BirthDate { get; set; } + public string ShortBio { get; set; } + + private Author() + { + /* This constructor is for deserialization / ORM purpose */ + } + + internal Author( + Guid id, + [NotNull] string name, + DateTime birthDate, + [CanBeNull] string shortBio = null) + : base(id) + { + SetName(name); + BirthDate = birthDate; + ShortBio = shortBio; + } + + internal Author ChangeName([NotNull] string name) + { + SetName(name); + return this; + } + + private void SetName([NotNull] string name) + { + Name = Check.NotNullOrWhiteSpace( + name, + nameof(name), + maxLength: AuthorConsts.MaxNameLength + ); + } + } +} +```` + +* 由 `FullAuditedAggregateRoot` 继承使得实体支持[软删除](../Data-Filtering.md) (指实体被删除时, 它并没有从数据库中被删除, 而只是被标记删除), 实体也具有了 [审计](../Entities.md) 属性. +* `Name` 属性的 `private set` 限制从类的外部设置这个属性. 有两种方法设置名字 (两种都进行了验证): + * 当新建一个作者时, 通过构造器. + * 使用 `ChangeName` 方法更新名字. +* `构造器` 和 `ChangeName` 方法的访问级别是 `internal`, 强制这些方法只能在领域层由 `AuthorManager` 使用. 稍后将对此进行解释. +* `Check` 类是一个ABP框架工具类, 用于检查方法参数 (如果参数非法会抛出 `ArgumentException`). + +`AuthorConsts` 是一个简单的类, 它位于 `Acme.BookStore.Domain.Shared` 项目的 `Authors` 命名空间 (文件夹)中: + +````csharp +namespace Acme.BookStore.Authors +{ + public static class AuthorConsts + { + public const int MaxNameLength = 64; + } +} +```` + +在 `Acme.BookStore.Domain.Shared` 项目中创建这个类, 因为[数据传输类](../Data-Transfer-Objects.md) (DTOs) 稍后会再一次用到它. + +## AuthorManager: 领域服务 + +`Author` 构造器和 `ChangeName` 方法的访问级别是 `internal`, 所以它们只能在领域层使用. 在 `Acme.BookStore.Domain` 项目中的 `Authors` 文件夹 (命名空间)创建 `AuthorManager` 类: + +````csharp +using System; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp; +using Volo.Abp.Domain.Services; + +namespace Acme.BookStore.Authors +{ + public class AuthorManager : DomainService + { + private readonly IAuthorRepository _authorRepository; + + public AuthorManager(IAuthorRepository authorRepository) + { + _authorRepository = authorRepository; + } + + public async Task CreateAsync( + [NotNull] string name, + DateTime birthDate, + [CanBeNull] string shortBio = null) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + + var existingAuthor = await _authorRepository.FindByNameAsync(name); + if (existingAuthor != null) + { + throw new AuthorAlreadyExistsException(name); + } + + return new Author( + GuidGenerator.Create(), + name, + birthDate, + shortBio + ); + } + + public async Task ChangeNameAsync( + [NotNull] Author author, + [NotNull] string newName) + { + Check.NotNull(author, nameof(author)); + Check.NotNullOrWhiteSpace(newName, nameof(newName)); + + var existingAuthor = await _authorRepository.FindByNameAsync(newName); + if (existingAuthor != null && existingAuthor.Id != author.Id) + { + throw new AuthorAlreadyExistsException(newName); + } + + author.ChangeName(newName); + } + } +} +```` + +* `AuthorManager` 强制使用一种可控的方式创建作者和修改作者的名字. 应用层 (后面会介绍) 将会使用这些方法. + +> **DDD 提示**: 如非必须并且用于执行核心业务规则, 不要引入领域服务方法. 对于这个场景, 我们使用这个服务保证名字的唯一性. + +两个方法都检查是否存在同名用户, 如果存在, 抛出业务异常 `AuthorAlreadyExistsException`, 这个异常定义在 `Acme.BookStore.Domain` 项目 (`Authors` 文件夹中): + +````csharp +using Volo.Abp; + +namespace Acme.BookStore.Authors +{ + public class AuthorAlreadyExistsException : BusinessException + { + public AuthorAlreadyExistsException(string name) + : base(BookStoreDomainErrorCodes.AuthorAlreadyExists) + { + WithData("name", name); + } + } +} +```` + +`BusinessException` 是一个特殊的异常类型. 在需要时抛出领域相关异常是一个好的实践. ABP框架会自动处理它, 并且它也容易本地化. `WithData(...)` 方法提供额外的数据给异常对象, 这些数据将会在本地化中或出于其它一些目的被使用. + +打开 `Acme.BookStore.Domain.Shared` 项目中的 `BookStoreDomainErrorCodes` 并修改为: + +````csharp +namespace Acme.BookStore +{ + public static class BookStoreDomainErrorCodes + { + public const string AuthorAlreadyExists = "BookStore:00001"; + } +} +```` + +这里定义了一个字符串, 表示应用程序抛出的错误码, 这个错误码可以被客户端应用程序处理. 为了用户, 你可能希望本地化它. 打开 `Acme.BookStore.Domain.Shared` 项目中的 `Localization/BookStore/en.json` , 加入以下项: + +````json +"BookStore:00001": "There is already an author with the same name: {name}" +```` + +当 `AuthorAlreadyExistsException` 被抛出, 终端用户将会在UI上看到组织好的错误消息. + +## IAuthorRepository + +`AuthorManager` 注入了 `IAuthorRepository`, 所以我们需要定义它. 在 `Acme.BookStore.Domain` 项目的 `Authors` 文件夹 (命名空间) 中创建这个新接口: + +````csharp +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace Acme.BookStore.Authors +{ + public interface IAuthorRepository : IRepository + { + Task FindByNameAsync(string name); + + Task> GetListAsync( + int skipCount, + int maxResultCount, + string sorting, + string filter = null + ); + } +} +```` + +* `IAuthorRepository` 扩展了标准 `IRepository` 接口, 所以所有的标准 [repository](../Repositories.md) 方法对于 `IAuthorRepository` 都是可用的. +* `FindByNameAsync` 在 `AuthorManager` 中用来根据姓名查询用户. +* `GetListAsync` 用于应用层以获得一个排序的, 经过过滤的作者列表, 显示在UI上. + +我们会在下一章实现这个repository. + +> 这两个方法似乎 **看上去没有必要**, 因为标准repositories已经是 `IQueryable`, 你可以直接使用它们, 而不是自定义方法. 在实际应用程序中, 这么做是没问题的. 但在这个 **学习指南**中, 解释如何在需要时创建自定义repository方法是有价值的. + +## 结论 + +这一章覆盖了图书管理程序作者相关功能的领域层. 在这一章中创建/更新的文件在下图中被高亮: + +![bookstore-author-domain-layer](images/bookstore-author-domain-layer.png) + +## 下一章 + +查看本教程的[下一章](Part-7.md). diff --git a/docs/zh-Hans/images/identity-module-menu.png b/docs/zh-Hans/images/identity-module-menu.png new file mode 100644 index 0000000000..ce2ee7e3e9 Binary files /dev/null and b/docs/zh-Hans/images/identity-module-menu.png differ diff --git a/docs/zh-Hans/images/identity-module-permissions.png b/docs/zh-Hans/images/identity-module-permissions.png new file mode 100644 index 0000000000..dfe0a9a022 Binary files /dev/null and b/docs/zh-Hans/images/identity-module-permissions.png differ diff --git a/docs/zh-Hans/images/identity-module-roles.png b/docs/zh-Hans/images/identity-module-roles.png new file mode 100644 index 0000000000..53df1cdc6d Binary files /dev/null and b/docs/zh-Hans/images/identity-module-roles.png differ diff --git a/docs/zh-Hans/images/identity-module-users.png b/docs/zh-Hans/images/identity-module-users.png new file mode 100644 index 0000000000..33edb4cb78 Binary files /dev/null and b/docs/zh-Hans/images/identity-module-users.png differ diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs index b92bb96f70..5c77dcff49 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Microsoft/AspNetCore/Components/WebAssembly/Hosting/AbpWebAssemblyHostBuilderExtensions.cs @@ -65,7 +65,7 @@ public static class AbpWebAssemblyHostBuilderExtensions return application; } - public async static Task InitializeAsync( + public async static Task InitializeApplicationAsync( [NotNull] this IAbpApplicationWithExternalServiceProvider application, [NotNull] IServiceProvider serviceProvider) { diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj b/framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj index 1b81b8e6d9..628661a184 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo.Abp.AspNetCore.Components.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpAspNetCoreComponentsModule.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpAspNetCoreComponentsModule.cs index 677f9d8cca..b73432b38a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpAspNetCoreComponentsModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpAspNetCoreComponentsModule.cs @@ -6,13 +6,14 @@ using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.ObjectMapping; using Volo.Abp.Security; +using Volo.Abp.Timing; namespace Volo.Abp.AspNetCore.Components; [DependsOn( typeof(AbpObjectMappingModule), typeof(AbpSecurityModule), - typeof(AbpLocalizationModule) + typeof(AbpTimingModule) )] public class AbpAspNetCoreComponentsModule : AbpModule { diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs index b9c97b30b4..66acd8f171 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs @@ -11,7 +11,9 @@ using Volo.Abp.AspNetCore.Components.ExceptionHandling; using Volo.Abp.AspNetCore.Components.Messages; using Volo.Abp.AspNetCore.Components.Notifications; using Volo.Abp.Localization; +using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; +using Volo.Abp.Timing; using Volo.Abp.Users; namespace Volo.Abp.AspNetCore.Components; @@ -54,6 +56,9 @@ public abstract class AbpComponentBase : OwningComponentBase protected ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser); private ICurrentUser _currentUser; + protected ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant); + private ICurrentTenant _currentTenant; + protected IUiMessageService Message => LazyGetNonScopedRequiredService(ref _message); private IUiMessageService _message; @@ -66,6 +71,9 @@ public abstract class AbpComponentBase : OwningComponentBase protected IAlertManager AlertManager => LazyGetNonScopedRequiredService(ref _alertManager); private IAlertManager _alertManager; + protected IClock Clock => LazyGetNonScopedRequiredService(ref _clock); + private IClock _clock; + protected AlertList Alerts => AlertManager.Alerts; protected IObjectMapper ObjectMapper { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js index 6f2a5cd727..fcdaca076d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js @@ -100,10 +100,10 @@ var abp = abp || {}; abp.event.on('abp.configurationInitialized', function () { var l = abp.localization.getResource('AbpUi'); + abp.libs.sweetAlert.config.default.confirmButtonText = l('Yes'); + abp.libs.sweetAlert.config.default.denyButtonText = l('No'); + abp.libs.sweetAlert.config.default.cancelButtonText = l('Cancel'); abp.libs.sweetAlert.config.confirm.title = l('AreYouSure'); - abp.libs.sweetAlert.config.confirm.confirmButtonText = l('Yes'); - abp.libs.sweetAlert.config.confirm.denyButtonText = l('No'); - abp.libs.sweetAlert.config.confirm.cancelButtonText = l('Cancel'); abp.libs.sweetAlert.config.confirm.showCancelButton = true; abp.libs.sweetAlert.config.confirm.reverseButtons = true; }); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index 705cc8fc65..b5204ae501 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -80,7 +80,7 @@ public class AspNetCoreApiDescriptionModelProvider : IApiDescriptionModelProvide var setting = FindSetting(controllerType); var moduleModel = applicationModel.GetOrAddModule( - GetRootPath(controllerType, setting), + GetRootPath(controllerType, apiDescription.ActionDescriptor, setting), GetRemoteServiceName(controllerType, setting) ); @@ -327,7 +327,9 @@ public class AspNetCoreApiDescriptionModelProvider : IApiDescriptionModelProvide return modelNameProvider.Name ?? parameterInfo.Name; } - private static string GetRootPath([NotNull] Type controllerType, + private static string GetRootPath( + [NotNull] Type controllerType, + [NotNull] ActionDescriptor actionDescriptor, [CanBeNull] ConventionalControllerSetting setting) { if (setting != null) @@ -335,7 +337,7 @@ public class AspNetCoreApiDescriptionModelProvider : IApiDescriptionModelProvide return setting.RootPath; } - var areaAttr = controllerType.GetCustomAttributes().OfType().FirstOrDefault(); + var areaAttr = controllerType.GetCustomAttributes().OfType().FirstOrDefault() ?? actionDescriptor.EndpointMetadata.OfType().FirstOrDefault(); if (areaAttr != null) { return areaAttr.RouteValue; diff --git a/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/AbpBackgroundJobsRabbitMqModule.cs b/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/AbpBackgroundJobsRabbitMqModule.cs index c601ca5549..a759479910 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/AbpBackgroundJobsRabbitMqModule.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/AbpBackgroundJobsRabbitMqModule.cs @@ -30,12 +30,12 @@ public class AbpBackgroundJobsRabbitMqModule : AbpModule public override void OnApplicationInitialization(ApplicationInitializationContext context) { - AsyncHelper.RunSync(() => StartJobQueueManagerAsync(context)); + AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context)); } public override void OnApplicationShutdown(ApplicationShutdownContext context) { - AsyncHelper.RunSync(() => StopJobQueueManagerAsync(context)); + AsyncHelper.RunSync(() => OnApplicationShutdownAsync(context)); } private async static Task StartJobQueueManagerAsync(ApplicationInitializationContext context) diff --git a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs index 47a165ca83..6c09a80f73 100644 --- a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs +++ b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/AbpBackgroundJobsModule.cs @@ -30,10 +30,6 @@ public class AbpBackgroundJobsModule : AbpModule public override void OnApplicationInitialization(ApplicationInitializationContext context) { - var options = context.ServiceProvider.GetRequiredService>().Value; - if (options.IsJobExecutionEnabled) - { - AsyncHelper.RunSync(() => context.AddBackgroundWorkerAsync()); - } + AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context)); } } diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs index 07586b461b..ff72cf138c 100644 --- a/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs +++ b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs @@ -52,16 +52,6 @@ public class AbpBackgroundWorkersQuartzModule : AbpModule public override void OnApplicationInitialization(ApplicationInitializationContext context) { - var quartzBackgroundWorkerOptions = context.ServiceProvider.GetRequiredService>().Value; - if (quartzBackgroundWorkerOptions.IsAutoRegisterEnabled) - { - var backgroundWorkerManager = context.ServiceProvider.GetRequiredService(); - var works = context.ServiceProvider.GetServices().Where(x => x.AutoRegister); - - foreach (var work in works) - { - AsyncHelper.RunSync(() => backgroundWorkerManager.AddAsync(work)); - } - } + AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context)); } } diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs index 32341f101e..12e24c8f81 100644 --- a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs +++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs @@ -35,27 +35,11 @@ public class AbpBackgroundWorkersModule : AbpModule public override void OnApplicationInitialization(ApplicationInitializationContext context) { - var options = context.ServiceProvider.GetRequiredService>().Value; - if (options.IsEnabled) - { - AsyncHelper.RunSync( - () => context.ServiceProvider - .GetRequiredService() - .StartAsync() - ); - } + AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context)); } public override void OnApplicationShutdown(ApplicationShutdownContext context) { - var options = context.ServiceProvider.GetRequiredService>().Value; - if (options.IsEnabled) - { - AsyncHelper.RunSync( - () => context.ServiceProvider - .GetRequiredService() - .StopAsync() - ); - } + AsyncHelper.RunSync(() => OnApplicationShutdownAsync(context)); } } diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobProviderSelector.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobProviderSelector.cs index bb32d573cc..1081125438 100644 --- a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobProviderSelector.cs +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobProviderSelector.cs @@ -30,7 +30,12 @@ public class DefaultBlobProviderSelector : IBlobProviderSelector, ITransientDepe if (!BlobProviders.Any()) { - throw new AbpException("No BLOB Storage provider was registered! At least one provider must be registered to be able to use the Blog Storing System."); + throw new AbpException("No BLOB Storage provider was registered! At least one provider must be registered to be able to use the BLOB Storing System."); + } + + if (configuration.ProviderType == null) + { + throw new AbpException("No BLOB Storage provider was used! At least one provider must be configured to be able to use the BLOB Storing System."); } foreach (var provider in BlobProviders) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj index 9acb67c7ea..ec983f6891 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj +++ b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj @@ -19,7 +19,7 @@ - + diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs index b4a873dcd4..30e2a9e5b0 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs @@ -40,8 +40,7 @@ public class AbpEventBusModule : AbpModule public override void OnApplicationInitialization(ApplicationInitializationContext context) { - AsyncHelper.RunSync(() => context.AddBackgroundWorkerAsync()); - AsyncHelper.RunSync(() => context.AddBackgroundWorkerAsync()); + AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context)); } private static void AddEventHandlers(IServiceCollection services) diff --git a/framework/src/Volo.Abp.Http.Client.Web/Volo/Abp/Http/Client/Web/Conventions/AbpHttpClientProxyServiceConvention.cs b/framework/src/Volo.Abp.Http.Client.Web/Volo/Abp/Http/Client/Web/Conventions/AbpHttpClientProxyServiceConvention.cs index b27e309740..3a0ef18d49 100644 --- a/framework/src/Volo.Abp.Http.Client.Web/Volo/Abp/Http/Client/Web/Conventions/AbpHttpClientProxyServiceConvention.cs +++ b/framework/src/Volo.Abp.Http.Client.Web/Volo/Abp/Http/Client/Web/Conventions/AbpHttpClientProxyServiceConvention.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Reflection; using Microsoft.AspNetCore.Mvc; @@ -50,6 +51,15 @@ public class AbpHttpClientProxyServiceConvention : AbpServiceConvention { controller.ControllerName = controller.ControllerName.RemovePostFix("ClientProxy"); + var moduleApiDescription = FindModuleApiDescriptionModel(controller); + + if (moduleApiDescription != null && !moduleApiDescription.RootPath.IsNullOrWhiteSpace()) + { + var selector = controller.Selectors.FirstOrDefault(); + selector?.EndpointMetadata.Add(new AreaAttribute(moduleApiDescription.RootPath)); + controller.RouteValues.Add(new KeyValuePair("area", moduleApiDescription.RootPath)); + } + var controllerApiDescription = FindControllerApiDescriptionModel(controller); if (controllerApiDescription != null && !controllerApiDescription.ControllerGroupName.IsNullOrWhiteSpace()) @@ -98,7 +108,7 @@ public class AbpHttpClientProxyServiceConvention : AbpServiceConvention var actionApiDescriptionModel = FindActionApiDescriptionModel(controller, action); if (actionApiDescriptionModel == null) { - return; ; + return;; } ActionWithAttributeRoute.Add(action); diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/AbpHttpClientProxyingOptions.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/AbpHttpClientProxyingOptions.cs index fbd57e40c3..d1cecaeb1a 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/AbpHttpClientProxyingOptions.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/AbpHttpClientProxyingOptions.cs @@ -9,9 +9,12 @@ public class AbpHttpClientProxyingOptions public Dictionary FormDataConverts { get; set; } + public Dictionary PathConverts { get; set; } + public AbpHttpClientProxyingOptions() { QueryStringConverts = new Dictionary(); FormDataConverts = new Dictionary(); + PathConverts = new Dictionary(); } } diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs index ab6cdb269e..abbbb8b6f1 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs @@ -112,8 +112,10 @@ public class ClientProxyRequestPayloadBuilder : ITransientDependency .MakeGenericMethod(value.GetType()) .Invoke(this, new object[] { - scope.ServiceProvider.GetRequiredService(HttpClientProxyingOptions.FormDataConverts[value.GetType()]), - value + scope.ServiceProvider.GetRequiredService(HttpClientProxyingOptions.FormDataConverts[value.GetType()]), + action, + parameter, + value }); if (formDataContents != null) @@ -154,7 +156,7 @@ public class ClientProxyRequestPayloadBuilder : ITransientDependency } else if (value.GetType().IsArray || (value.GetType().IsGenericType && value is IEnumerable)) { - foreach (var item in (IEnumerable)value) + foreach (var item in (IEnumerable) value) { formData.Add(new StringContent(item.ToString(), Encoding.UTF8), parameter.Name); } @@ -168,8 +170,8 @@ public class ClientProxyRequestPayloadBuilder : ITransientDependency return formData; } - protected virtual async Task>> ObjectToFormDataAsync(IObjectToFormData converter, T value) + protected virtual async Task>> ObjectToFormDataAsync(IObjectToFormData converter, ActionApiDescriptionModel actionApiDescription, ParameterApiDescriptionModel parameterApiDescription, T value) { - return await converter.ConvertAsync(value); + return await converter.ConvertAsync(actionApiDescription, parameterApiDescription, value); } } diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs index ce962bee14..63ddd75510 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs @@ -21,11 +21,17 @@ public class ClientProxyUrlBuilder : ITransientDependency { protected static MethodInfo CallObjectToQueryStringAsyncMethod { get; } + protected static MethodInfo CallObjectToPathAsyncMethod { get; } + static ClientProxyUrlBuilder() { CallObjectToQueryStringAsyncMethod = typeof(ClientProxyUrlBuilder) .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) .First(m => m.Name == nameof(ObjectToQueryStringAsync) && m.IsGenericMethodDefinition); + + CallObjectToPathAsyncMethod = typeof(ClientProxyUrlBuilder) + .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) + .First(m => m.Name == nameof(ObjectToPathAsync) && m.IsGenericMethodDefinition); } protected IServiceScopeFactory ServiceScopeFactory { get; } @@ -46,22 +52,22 @@ public class ClientProxyUrlBuilder : ITransientDependency { var urlBuilder = new StringBuilder(action.Url); - await ReplacePathVariablesAsync(urlBuilder, action.Parameters, methodArguments, apiVersion); - await AddQueryStringParametersAsync(urlBuilder, action.Parameters, methodArguments, apiVersion); + await ReplacePathVariablesAsync(urlBuilder, action, methodArguments, apiVersion); + await AddQueryStringParametersAsync(urlBuilder, action, methodArguments, apiVersion); return urlBuilder.ToString(); } } - protected virtual Task ReplacePathVariablesAsync(StringBuilder urlBuilder, IList actionParameters, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) + protected virtual async Task ReplacePathVariablesAsync(StringBuilder urlBuilder, ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) { - var pathParameters = actionParameters + var pathParameters = action.Parameters .Where(p => p.BindingSourceId == ParameterBindingSources.Path) .ToArray(); if (!pathParameters.Any()) { - return Task.CompletedTask; + return; } if (pathParameters.Any(p => p.Name == "apiVersion")) @@ -81,7 +87,7 @@ public class ClientProxyUrlBuilder : ITransientDependency } else if (pathParameter.DefaultValue != null) { - urlBuilder = urlBuilder.Replace($"{{{pathParameter.Name}}}", pathParameter.DefaultValue.ToString()); + urlBuilder = urlBuilder.Replace($"{{{pathParameter.Name}}}", await ConvertValueToStringAsync(pathParameter.DefaultValue)); } else { @@ -90,16 +96,36 @@ public class ClientProxyUrlBuilder : ITransientDependency } else { - urlBuilder = urlBuilder.Replace($"{{{pathParameter.Name}}}", value.ToString()); + if (HttpClientProxyingOptions.PathConverts.ContainsKey(value.GetType())) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var path = await (Task)CallObjectToPathAsyncMethod + .MakeGenericMethod(value.GetType()) + .Invoke(this, new object[] + { + scope.ServiceProvider.GetRequiredService(HttpClientProxyingOptions.PathConverts[value.GetType()]), + action, + pathParameter, + value + }); + + if (path != null) + { + urlBuilder = urlBuilder.Replace($"{{{pathParameter.Name}}}", path); + continue; + } + } + } + + urlBuilder = urlBuilder.Replace($"{{{pathParameter.Name}}}", await ConvertValueToStringAsync(value)); } } - - return Task.CompletedTask; } - protected virtual async Task AddQueryStringParametersAsync(StringBuilder urlBuilder, IList actionParameters, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) + protected virtual async Task AddQueryStringParametersAsync(StringBuilder urlBuilder, ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) { - var queryStringParameters = actionParameters + var queryStringParameters = action.Parameters .Where(p => p.BindingSourceId.IsIn(ParameterBindingSources.ModelBinding, ParameterBindingSources.Query)) .ToArray(); @@ -121,8 +147,10 @@ public class ClientProxyUrlBuilder : ITransientDependency .MakeGenericMethod(value.GetType()) .Invoke(this, new object[] { - scope.ServiceProvider.GetRequiredService(HttpClientProxyingOptions.QueryStringConverts[value.GetType()]), - value + scope.ServiceProvider.GetRequiredService(HttpClientProxyingOptions.QueryStringConverts[value.GetType()]), + action, + queryStringParameter, + value }); if (queryString != null) @@ -147,9 +175,14 @@ public class ClientProxyUrlBuilder : ITransientDependency } } - protected virtual async Task ObjectToQueryStringAsync(IObjectToQueryString converter, T value) + protected virtual async Task ObjectToQueryStringAsync(IObjectToQueryString converter, ActionApiDescriptionModel actionApiDescription, ParameterApiDescriptionModel parameterApiDescription, T value) + { + return await converter.ConvertAsync(actionApiDescription, parameterApiDescription, value); + } + + protected virtual async Task ObjectToPathAsync(IObjectToPath converter, ActionApiDescriptionModel actionApiDescription, ParameterApiDescriptionModel parameterApiDescription, T value) { - return await converter.ConvertAsync(value); + return await converter.ConvertAsync(actionApiDescription, parameterApiDescription, value); } protected virtual async Task AddQueryStringParameterAsync( @@ -161,7 +194,7 @@ public class ClientProxyUrlBuilder : ITransientDependency if (value.GetType().IsArray || (value.GetType().IsGenericType && value is IEnumerable)) { var index = 0; - foreach (var item in (IEnumerable)value) + foreach (var item in (IEnumerable) value) { if (index == 0) { @@ -194,4 +227,4 @@ public class ClientProxyUrlBuilder : ITransientDependency return Task.FromResult(value?.ToString()); } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToFormData.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToFormData.cs index b98b8c8ba7..695ca888ba 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToFormData.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToFormData.cs @@ -1,10 +1,11 @@ using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; +using Volo.Abp.Http.Modeling; namespace Volo.Abp.Http.Client.ClientProxying; public interface IObjectToFormData { - Task>> ConvertAsync(TValue value); + Task>> ConvertAsync(ActionApiDescriptionModel actionApiDescription, ParameterApiDescriptionModel parameterApiDescription, TValue value); } diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToPath.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToPath.cs new file mode 100644 index 0000000000..2da0a3b90e --- /dev/null +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToPath.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Volo.Abp.Http.Modeling; + +namespace Volo.Abp.Http.Client.ClientProxying +{ + public interface IObjectToPath + { + Task ConvertAsync(ActionApiDescriptionModel actionApiDescription, ParameterApiDescriptionModel parameterApiDescription, TValue value); + } +} diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToQueryString.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToQueryString.cs index 7d1a84ed80..0e9008b1ed 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToQueryString.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToQueryString.cs @@ -1,8 +1,9 @@ using System.Threading.Tasks; +using Volo.Abp.Http.Modeling; namespace Volo.Abp.Http.Client.ClientProxying; public interface IObjectToQueryString { - Task ConvertAsync(TValue value); + Task ConvertAsync(ActionApiDescriptionModel actionApiDescription, ParameterApiDescriptionModel parameterApiDescription, TValue value); } diff --git a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs b/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs index db790a717d..10e09444c5 100644 --- a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs +++ b/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs @@ -81,18 +81,11 @@ public class AbpQuartzModule : AbpModule public override void OnApplicationInitialization(ApplicationInitializationContext context) { - var options = context.ServiceProvider.GetRequiredService>().Value; - - _scheduler = context.ServiceProvider.GetRequiredService(); - - AsyncHelper.RunSync(() => options.StartSchedulerFactory.Invoke(_scheduler)); + AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context)); } public override void OnApplicationShutdown(ApplicationShutdownContext context) { - if (_scheduler.IsStarted) - { - AsyncHelper.RunSync(() => _scheduler.Shutdown()); - } + AsyncHelper.RunSync(() => OnApplicationShutdownAsync(context)); } } diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs index 66962962c7..6540b717d3 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs @@ -59,6 +59,7 @@ public class AbpHttpClientTestModule : AbpModule { options.QueryStringConverts.Add(typeof(List), typeof(TestObjectToQueryString)); options.FormDataConverts.Add(typeof(List), typeof(TestObjectToFormData)); + options.PathConverts.Add(typeof(int), typeof(TestObjectToPath)); }); } } diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs index 18ea2d3271..547f1bf5a7 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs @@ -26,6 +26,10 @@ public interface IRegularTestController Task GetObjectandIdAsync(int id, Car bodyValue); + Task GetObjectandFirstReleaseDateAsync(DateTime time, Car bodyValue); + + Task GetObjectandCountAsync(int count, Car bodyValue); + Task GetObjectAndIdWithQueryAsync(int id, Car bodyValue); Task PutValueWithBodyAsync(string bodyValue); diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs index e9443f80ca..b092e67bc8 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs @@ -10,7 +10,7 @@ namespace Volo.Abp.Http.DynamicProxying; [Route("api/regular-test-controller")] [RemoteService] //Automatically enables API explorer and apply ABP conventions. - //[ApiExplorerSettings(IgnoreApi = false)] //alternative +//[ApiExplorerSettings(IgnoreApi = false)] //alternative public class RegularTestController : AbpController, IRegularTestController { [HttpGet] @@ -32,7 +32,7 @@ public class RegularTestController : AbpController, IRegularTestController public Task GetException2Async() { throw new BusinessException("Volo.Abp.Http.DynamicProxying:10001") - .WithData("0", "TEST"); + .WithData("0","TEST"); } [HttpGet] @@ -86,6 +86,22 @@ public class RegularTestController : AbpController, IRegularTestController return Task.FromResult(bodyValue); } + [HttpGet] + [Route("post-object-and-first-release-date-with-url/{time:datetime}")] + public Task GetObjectandFirstReleaseDateAsync(DateTime time, Car bodyValue) + { + bodyValue.FirstReleaseDate = time; + return Task.FromResult(bodyValue); + } + + [HttpGet] + [Route("post-object-and-count-with-url/{count}")] + public Task GetObjectandCountAsync(int count, Car bodyValue) + { + bodyValue.Year = count; + return Task.FromResult(bodyValue); + } + [HttpGet] [Route("post-object-and-id-with-url-and-query/{id}")] public Task GetObjectAndIdWithQueryAsync(int id, Car bodyValue) diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs index 338654a7bc..519e319f26 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs @@ -124,6 +124,23 @@ public class RegularTestControllerClientProxy_Tests : AbpHttpClientTestBase result.Model.ShouldBe("Ford"); } + [Fact] + public async Task GetObjectandFirstReleaseDateAsync() + { + var time = DateTime.Now; + var result = await _controller.GetObjectandFirstReleaseDateAsync(time, new Car { Year = 1976, Model = "Ford", FirstReleaseDate = new DateTime(1976, 02, 22, 15, 0, 6, 22) }); + result.FirstReleaseDate.ToUniversalTime().ShouldBe(time.ToUniversalTime()); + result.Model.ShouldBe("Ford"); + } + + [Fact] + public async Task GetObjectandCountAsync() + { + var result = await _controller.GetObjectandCountAsync(-1, new Car { Year = 1976, Model = "Ford", FirstReleaseDate = new DateTime(1976, 02, 22, 15, 0, 6, 22) }); + result.Year.ShouldBe(888); + result.Model.ShouldBe("Ford"); + } + [Fact] public async Task GetObjectAndIdWithQueryAsync() { diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToFormData.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToFormData.cs index f67337b601..2c64773c88 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToFormData.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToFormData.cs @@ -4,13 +4,14 @@ using System.Text; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Client.ClientProxying; +using Volo.Abp.Http.Modeling; using Volo.Abp.TestApp.Application.Dto; namespace Volo.Abp.Http.DynamicProxying; public class TestObjectToFormData : IObjectToFormData>, ITransientDependency { - public Task>> ConvertAsync(List values) + public Task>> ConvertAsync(ActionApiDescriptionModel actionApiDescription, ParameterApiDescriptionModel parameterApiDescription, List values) { if (values.IsNullOrEmpty()) { diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToPath.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToPath.cs new file mode 100644 index 0000000000..a9b7936578 --- /dev/null +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToPath.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Http.Client.ClientProxying; +using Volo.Abp.Http.Modeling; + +namespace Volo.Abp.Http.DynamicProxying; + +public class TestObjectToPath : IObjectToPath, ITransientDependency +{ + public Task ConvertAsync(ActionApiDescriptionModel actionApiDescription, ParameterApiDescriptionModel parameterApiDescription, int value) + { + if (actionApiDescription.Name == nameof(IRegularTestController.GetObjectandCountAsync)) + { + if (value <= 0) + { + value = 888; + } + return Task.FromResult(value.ToString()); + } + + return Task.FromResult(null); + } +} diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToQueryString.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToQueryString.cs index 7537dd8701..d4ca6f7285 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToQueryString.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToQueryString.cs @@ -3,13 +3,14 @@ using System.Text; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Client.ClientProxying; +using Volo.Abp.Http.Modeling; using Volo.Abp.TestApp.Application.Dto; namespace Volo.Abp.Http.DynamicProxying; public class TestObjectToQueryString : IObjectToQueryString>, ITransientDependency { - public Task ConvertAsync(List values) + public Task ConvertAsync(ActionApiDescriptionModel actionApiDescription, ParameterApiDescriptionModel parameterApiDescription, List values) { if (values.IsNullOrEmpty()) { diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json index fc16c7ed61..284b579588 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json @@ -3,7 +3,7 @@ "name": "asp.net", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.shared": "^5.0.0", + "@abp/aspnetcore.mvc.ui.theme.shared": "^5.0.1", "highlight.js": "^9.13.1" }, "devDependencies": {} diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock index 39b0bb0b53..c370d892cd 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock @@ -2,30 +2,30 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.shared@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-5.0.0.tgz#e1dbcd384f9e873fec6ca678ef37262027ad5812" - integrity sha512-9qCvKNYxMKkvmkSFciP2fxKgVfBGZbxfXB0qi2bhSmhzUMyu1mizQ+q9wvXoJMFvwRTmCwuUxNwMRyZpEwbElw== - dependencies: - "@abp/aspnetcore.mvc.ui" "~5.0.0" - "@abp/bootstrap" "~5.0.0" - "@abp/bootstrap-datepicker" "~5.0.0" - "@abp/datatables.net-bs5" "~5.0.0" - "@abp/font-awesome" "~5.0.0" - "@abp/jquery-form" "~5.0.0" - "@abp/jquery-validation-unobtrusive" "~5.0.0" - "@abp/lodash" "~5.0.0" - "@abp/luxon" "~5.0.0" - "@abp/malihu-custom-scrollbar-plugin" "~5.0.0" - "@abp/select2" "~5.0.0" - "@abp/sweetalert2" "~5.0.0" - "@abp/timeago" "~5.0.0" - "@abp/toastr" "~5.0.0" - -"@abp/aspnetcore.mvc.ui@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-5.0.0.tgz#3a48cac9cd2c487023c61fa27109abe6ec8eb276" - integrity sha512-6HWPgdcg/mcLPrgV+p/hfkQO3s1De2qCUlvUVBWght4FAM0N0fblfehVgglGzXjR5GrQOTO+HZMogSybQZe5IA== +"@abp/aspnetcore.mvc.ui.theme.shared@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-5.0.1.tgz#77a4900424603e0be048f3507aa852701f3139a2" + integrity sha512-mn9HEOHBkmBr7d/VUt0bfg9K58IyRDb7j3ZiDoHo263WLXJkf0+C7MEbv/zxfDHW4NeztsNCd3ugA7PdTxPzmA== + dependencies: + "@abp/aspnetcore.mvc.ui" "~5.0.1" + "@abp/bootstrap" "~5.0.1" + "@abp/bootstrap-datepicker" "~5.0.1" + "@abp/datatables.net-bs5" "~5.0.1" + "@abp/font-awesome" "~5.0.1" + "@abp/jquery-form" "~5.0.1" + "@abp/jquery-validation-unobtrusive" "~5.0.1" + "@abp/lodash" "~5.0.1" + "@abp/luxon" "~5.0.1" + "@abp/malihu-custom-scrollbar-plugin" "~5.0.1" + "@abp/select2" "~5.0.1" + "@abp/sweetalert2" "~5.0.1" + "@abp/timeago" "~5.0.1" + "@abp/toastr" "~5.0.1" + +"@abp/aspnetcore.mvc.ui@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-5.0.1.tgz#f5aa4e0dcc10878f590e30836887f3f9861e91e3" + integrity sha512-bN15UcNhTqeo0RXlIYws7Jd8ZfSBsVM6Y5wiAqbq1Q3Lvs1uLmryhZpn6sHmKw2Z3Lnhec7VjnZN0FwNpzEqUw== dependencies: ansi-colors "^4.1.1" extend-object "^1.0.0" @@ -34,145 +34,145 @@ merge-stream "^2.0.0" micromatch "^4.0.2" -"@abp/bootstrap-datepicker@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-5.0.0.tgz#b830c5932596ecd0d58291b9f3c8e12aaaee6d2a" - integrity sha512-Xxad5FKwwDM7mCbgQaKkPwenGquAjWwy0En0Wbmp6L+rkrssTFMyeeF2UooK/I4DbvSwsY/GmxhscL68B0rJ0g== +"@abp/bootstrap-datepicker@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-5.0.1.tgz#52116a154e343d84c87257ad1ef10b54012f4c33" + integrity sha512-ZJWaZKzGzshBAfyBqaUScfWqeBVfWGbZIwIplF8VKtDGcyI8R54bqDsydhS88CCnolRz4+tfq3QY6yAXrUEKAw== dependencies: bootstrap-datepicker "^1.9.0" -"@abp/bootstrap@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-5.0.0.tgz#3f307f9ae23df3193451ce6d9496ae1ed0358fbd" - integrity sha512-Se4aAa6fmwrv/T0TR+4DgCBicPrw3Qz/ybs6lbqPXz4HNOS1c/dMRZowARWZie7AqoI7mctGo0DeBikNOxxndQ== +"@abp/bootstrap@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-5.0.1.tgz#d542a8fd7151e22eda1643e313a67f9892c50533" + integrity sha512-wyVAp6a4OIwlqK7AZozs7wpsBZgzG55EKeChBBfUXloJPuirQHxccFNE41aAYWfKur+wj25WD5NBnzFuKyNYiQ== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" bootstrap "^5.1.0" bootstrap-v4-rtl "4.6.0-1" -"@abp/core@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-5.0.0.tgz#ef8097bf581ecc0f3040ab448d55c4bd60002211" - integrity sha512-C5E33KS39xI9nYFwKlK2TuON5j8SX5CnBYuV9ijiMhBKjCz/8+9GYXbsZnS6wR+YwgDoLlAAntBT2o5PkmditA== +"@abp/core@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-5.0.1.tgz#904fa2c22bcbf42deb56abf772c3a53362807a2f" + integrity sha512-QzX35Oa+Vncu2OP1tXvGjAVFznmD1tZRn4tlDQgn36B2MU06UEO0JSx7JVedPvhzQZceUe5+WWMfuYNM2xeBTw== dependencies: - "@abp/utils" "~5.0.0" + "@abp/utils" "~5.0.1" -"@abp/datatables.net-bs5@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-5.0.0.tgz#1ad946ebaf96ec9847c743dac072cf9cb141830c" - integrity sha512-7J4brA7ocIA8Eytwie9iyDTHB19H3TZNpLejs0jFNnD2Uwf6ESL/SvojeNDmfDJZ+a9v5COTcXmt+as3CNxHng== +"@abp/datatables.net-bs5@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-5.0.1.tgz#b8afa2255e374c86c50e57bd57c2b36a86d6a890" + integrity sha512-1fuCk+K4xW3XnDFo2+kPXHMoOTol4HLRcWVRELjoBQ5YbTxEPsbrhfNUeUsECDAmkxoqY7G3MEdtIVwmy5NPpg== dependencies: - "@abp/datatables.net" "~5.0.0" + "@abp/datatables.net" "~5.0.1" datatables.net-bs5 "^1.10.25" -"@abp/datatables.net@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-5.0.0.tgz#0d319a7b1e902d824219fd8f8d05d62c5cd1ee50" - integrity sha512-FUQgmfbSNxWnUwBbkhc5ZJkIsnqeVjpzzObllrSwlj7TNhp79KA6sa5KFp7D8RVYFp5aVvPFRUO4mZksyjXByg== +"@abp/datatables.net@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-5.0.1.tgz#0ce5bdf7aa5bf224b93a22758a016c50562d6569" + integrity sha512-x9D2YNtnOJRrKXi2Pwrqzz9sIOrn3o6x2c8ZxjyC54vh+SLQtLycAzQw7OyaZGdbFkudlJiCuME/FiuZvht8Og== dependencies: - "@abp/jquery" "~5.0.0" + "@abp/jquery" "~5.0.1" datatables.net "^1.10.21" -"@abp/font-awesome@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-5.0.0.tgz#a7c92726e255745324fa8e691ec406a6b76542e0" - integrity sha512-DQQr/+7PrnxmEZAc30Hdt7G8y8MT3y9V0xRoIfIMyjk1Dwv/7kO6xN/eXu4pxPC1BSGGj5nfMgEYxl6K/+4C6w== +"@abp/font-awesome@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-5.0.1.tgz#661753403499925661f880c53214841dd34f5210" + integrity sha512-C+alReNvHZzST55ITBTyFqhdTehPo5ondzfqc9QlyB3Kwk1DIq0CaLqfS2xU5glYGDSqKi7xfkyQnJdwu2hKRA== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" "@fortawesome/fontawesome-free" "^5.13.0" -"@abp/jquery-form@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-5.0.0.tgz#f24e3e7191c2a7221352ee916cebf8d7f1e50ace" - integrity sha512-N8u+JRDrwMkvdZrQE8c/JaOMFFFvxuF6GnTyXARxEFFuzB7PnYPj2s+kFvyFMFRQgFQjfwUU90H0WBNsDW5u9A== +"@abp/jquery-form@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-5.0.1.tgz#20fa51946f68a7acd6ef3dd3d8d1349307f681b4" + integrity sha512-Q7GG0+XFXs7pDoa4p3r7vm8+sUh5ohwovedPNVghtX8Rm2I1sfVfJKamPr24PCAyJcFVntU1WCt/CngneES3pw== dependencies: - "@abp/jquery" "~5.0.0" + "@abp/jquery" "~5.0.1" jquery-form "^4.3.0" -"@abp/jquery-validation-unobtrusive@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-5.0.0.tgz#65c618be30983884f3d8d6de1cf3cf39dbd98a6a" - integrity sha512-i7X+Zz4upwJYEczSWlZS7Zxt+3HeHBGVjAcO0Tk3/oefd6qXpK2MIsIqy6icAutSM0oMsN0uLsJwuEx8vRqfyg== +"@abp/jquery-validation-unobtrusive@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-5.0.1.tgz#50798d3c087d0b103944b3a9e7c39a1f51db9fdc" + integrity sha512-w5SAYB0EZRbOhIFcCUXWvoGB/dWj3zyQOarbvzJ9LelgrXvvoissaaSSqq6BySjr1UigIWBCVuruAQssizmXBQ== dependencies: - "@abp/jquery-validation" "~5.0.0" + "@abp/jquery-validation" "~5.0.1" jquery-validation-unobtrusive "^3.2.11" -"@abp/jquery-validation@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-5.0.0.tgz#b28e7951d322babf46178e8007caa702d6c058a7" - integrity sha512-UI/P+NHBtYbJbSTDAyL84F9Pt/yy89NZVF4TZO9nspcn1Q9CbvC8AjETrHnp4m8n+HEckiuSMZNgkEy6j1hSwQ== +"@abp/jquery-validation@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-5.0.1.tgz#5932060d1fdf04a81e998caac959188aefdf1669" + integrity sha512-T5p0U2cWh6Jov55AiUxJYRw92L6ubFXfqL0EkaePV5AnoetVXheluhq9gQAKnwVAqtc/3Rxkr/lO047catUkjw== dependencies: - "@abp/jquery" "~5.0.0" + "@abp/jquery" "~5.0.1" jquery-validation "^1.19.2" -"@abp/jquery@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-5.0.0.tgz#8f61c41fa199a59f49f4a1e149c105976976ca93" - integrity sha512-h9TZRQGnOb4ZwKn80LCJRNRKeTETjGNm6zv25mvz+/IGbC+YMybe5xRnzZkgIXQKsd7zC0DumYOj3VENUuo8RA== +"@abp/jquery@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-5.0.1.tgz#1522ba1e0c488bffdc30a230ab35e225ae32d028" + integrity sha512-AacK4cy/RpzBfRYf7Jjn+TmVm72TW0jEjRoQ3TOXmlOrunRph05mQm72Ux2o8+Z1lOwxJKRz5gKSbRTfa6Tgpw== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" jquery "~3.6.0" -"@abp/lodash@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-5.0.0.tgz#bef626cd13e4c18fb24f0323a4fccc018ea91d6b" - integrity sha512-vFub55GnexnGqVuea7CXjJkPWXsEYRIKds195vmgDVjqKwNJlWAQENtuxRT7PiYIfywJWxJdokqZET2ZEsixTg== +"@abp/lodash@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-5.0.1.tgz#ad22895772ce65e52941dddb84d0a7d26c521d59" + integrity sha512-r1QqZCXfDd0YQnE4MkcY291H5jWFdZtGeYmwjIkn7aeOZ0PP9qsDPkWTwPwbpfmnz4DhmIdO5a1rveWz/J5ZwQ== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" lodash "^4.17.15" -"@abp/luxon@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-5.0.0.tgz#3dbaa82fbf73955839fb58dc93ad850786a58133" - integrity sha512-VilSmjNzFlcvYZHL+6VSfOnZPrGKA9TFvcxzaGRMG4sTLtT+1Ag0TMKF6aCe0Dslf6D1QwRDlMbikMXmHR99gA== +"@abp/luxon@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-5.0.1.tgz#7930e30db60e63b486a51030c60e09b54bcb3867" + integrity sha512-cXbnyAI9Gn2bZBQuCN46UFOkzb+djA3LMgpeMlTTv0aA87PrjzT60EPQ4a9FLovESqNnblaQ9Je43QwoGH8kZA== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" luxon "^1.24.1" -"@abp/malihu-custom-scrollbar-plugin@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-5.0.0.tgz#29eeb3a701195ae511ec7334940dba42ec206d69" - integrity sha512-g2mYagJwq92qA+PlIgqPD6PMJrznHgSM+Ixi/1VnVAKr+G6rNReo3wzrDOrkohXeenFsjRHIPnP/BOJw2mVwvg== +"@abp/malihu-custom-scrollbar-plugin@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-5.0.1.tgz#268ec30eef8ba72a0c8cb997e55d48a1508b2719" + integrity sha512-mqF6OizoWp6kg5pztNfF78+mbfyX3JAXDfv+ZUCvjtAqllIzyOUIqRSXULDkAcDeCC1+PluRsd+uSltIppC5hA== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/select2@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-5.0.0.tgz#dd56d841807a9c989a1dbc8bbb8279edd3ec8106" - integrity sha512-u2ggKeYLvUWYRVVNUeoaB/R1YAqLXMj6TfcdidWxrhLFvpjkerCRx2dholy0cAwAyAWssR4LCF/pANNcN7YTbg== +"@abp/select2@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-5.0.1.tgz#644c46c7c942a6b9d36bc76c7132f32ad577933b" + integrity sha512-FHVqEebQS3XiYn5TMEhGaQJOAYnPqxivKpeOB0F0F2bEDGqt3OXh05KcQBOm+sDSScq3c7T7HLr9mMzlYtyHXw== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" select2 "^4.0.13" -"@abp/sweetalert2@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-5.0.0.tgz#54ef957bc73c990336376e0a1978c5b1e857fcb7" - integrity sha512-yF3sddxZkhwVxjdMUgZsWpMkgei9VKVRuNm363Cz61mmtznx/c2Y243GNGkA5cwcAr+Q0pZkONlngAfDm2ifkw== +"@abp/sweetalert2@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-5.0.1.tgz#acf2acfc04619c7907237238bfaeb63f2d75fb22" + integrity sha512-CPqXIaHK7Ff6i8gNhxoZ9r3te9LIEiDCUpdHNtgRVMCaLbywfkiLyirlfRIB6lxRWSw8BZ+M/azdp5uzidW8Vw== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" sweetalert2 "^11.0.18" -"@abp/timeago@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-5.0.0.tgz#e0d71d213d1dc64850d77e80ab13c6964bc8d03b" - integrity sha512-rpBmVKQ7fZeqV29Ls/+oIgshNqnj2qZySbNsx193ybruCr7JCiRgTXJ+Vig+Zox+3nTAZv8uDBN5VDZn7aSXvA== +"@abp/timeago@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-5.0.1.tgz#09ca6302304de09baa5ac8c72b950515ab0d0451" + integrity sha512-6onXho8R6eiWfncONZFBn18OwgQPKIaRM73rVevpRGJTf2pylcHtCp4uHGsoqgCc+TQU7FyoKTTkg4lfvCQa0A== dependencies: - "@abp/jquery" "~5.0.0" + "@abp/jquery" "~5.0.1" timeago "^1.6.7" -"@abp/toastr@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-5.0.0.tgz#b413d79b47cab2a4f8f72b98128ca8f22436c36a" - integrity sha512-YZSboTPXzQjZ1hR18VWIvMN+H8Q0J3DMrCtn6auGJ2fEJGcTXWjQViZc6XEKvat6Ms96tDCuQVf0Z3OfeLdX3Q== +"@abp/toastr@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-5.0.1.tgz#a3959df1de0c4640fabf508da9ccd3ed37ae2b03" + integrity sha512-ndL1uj2ce4PbGDzxh+QCIWYu+GAmW1Xwma4aJcq5zKo+xhlLJJnTe6QFGKr7wSsTVZMB7z1Pp2olvXH6an82+g== dependencies: - "@abp/jquery" "~5.0.0" + "@abp/jquery" "~5.0.1" toastr "^2.1.4" -"@abp/utils@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-5.0.0.tgz#2297601a8aff931c6eb2d62fac6a9756312e4ad8" - integrity sha512-dSbGhJE+Rs/V7VCdWgOV/6y59eXkbKMBaF88bvdDSR0QEwVpH0Fd18lu3gQhw/ENEPyd6kw9zqqnesToNw8lXg== +"@abp/utils@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-5.0.1.tgz#57934383004a21172bf7e08130813c08946894fb" + integrity sha512-WLCWe7iJ8krxsz8Z7E+8GDaGZuG/6PZDdqPcMIh95opi0kvcLxb+mjPXXpiXdtFzW7O09a+NaNOxorsEFxjoKw== dependencies: just-compare "^1.3.0" diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json index 8665ad48ec..61ec92cd1d 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json @@ -3,8 +3,8 @@ "name": "asp.net", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "^5.0.0", - "@abp/prismjs": "^5.0.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "^5.0.1", + "@abp/prismjs": "^5.0.1" }, "devDependencies": {} } diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock index 0aeb35deed..1146061d9c 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock @@ -2,37 +2,37 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-5.0.0.tgz#67027b520d06f6b5e0d486d5af747886d9dda237" - integrity sha512-+L4ea4G8FIOSDjTU63IqnSVjN4SQsRH+by8lRUcafSXYUi9yGXt039OUUr/6T/tnWVztZab7GiBmCwGMkV8ZKA== +"@abp/aspnetcore.mvc.ui.theme.basic@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-5.0.1.tgz#884a4bc9ea043654891be56cd7e7c7bbf9bbad8c" + integrity sha512-JGTQyg3Zyfda84yg/Geq6rGuBN2SX4gIOaFL2h8BX9bA8CVuBC+zcwfO1bBsP89xfTyPvdSAGBvSfhCjPwsukQ== dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~5.0.0" + "@abp/aspnetcore.mvc.ui.theme.shared" "~5.0.1" -"@abp/aspnetcore.mvc.ui.theme.shared@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-5.0.0.tgz#e1dbcd384f9e873fec6ca678ef37262027ad5812" - integrity sha512-9qCvKNYxMKkvmkSFciP2fxKgVfBGZbxfXB0qi2bhSmhzUMyu1mizQ+q9wvXoJMFvwRTmCwuUxNwMRyZpEwbElw== - dependencies: - "@abp/aspnetcore.mvc.ui" "~5.0.0" - "@abp/bootstrap" "~5.0.0" - "@abp/bootstrap-datepicker" "~5.0.0" - "@abp/datatables.net-bs5" "~5.0.0" - "@abp/font-awesome" "~5.0.0" - "@abp/jquery-form" "~5.0.0" - "@abp/jquery-validation-unobtrusive" "~5.0.0" - "@abp/lodash" "~5.0.0" - "@abp/luxon" "~5.0.0" - "@abp/malihu-custom-scrollbar-plugin" "~5.0.0" - "@abp/select2" "~5.0.0" - "@abp/sweetalert2" "~5.0.0" - "@abp/timeago" "~5.0.0" - "@abp/toastr" "~5.0.0" - -"@abp/aspnetcore.mvc.ui@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-5.0.0.tgz#3a48cac9cd2c487023c61fa27109abe6ec8eb276" - integrity sha512-6HWPgdcg/mcLPrgV+p/hfkQO3s1De2qCUlvUVBWght4FAM0N0fblfehVgglGzXjR5GrQOTO+HZMogSybQZe5IA== +"@abp/aspnetcore.mvc.ui.theme.shared@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-5.0.1.tgz#77a4900424603e0be048f3507aa852701f3139a2" + integrity sha512-mn9HEOHBkmBr7d/VUt0bfg9K58IyRDb7j3ZiDoHo263WLXJkf0+C7MEbv/zxfDHW4NeztsNCd3ugA7PdTxPzmA== + dependencies: + "@abp/aspnetcore.mvc.ui" "~5.0.1" + "@abp/bootstrap" "~5.0.1" + "@abp/bootstrap-datepicker" "~5.0.1" + "@abp/datatables.net-bs5" "~5.0.1" + "@abp/font-awesome" "~5.0.1" + "@abp/jquery-form" "~5.0.1" + "@abp/jquery-validation-unobtrusive" "~5.0.1" + "@abp/lodash" "~5.0.1" + "@abp/luxon" "~5.0.1" + "@abp/malihu-custom-scrollbar-plugin" "~5.0.1" + "@abp/select2" "~5.0.1" + "@abp/sweetalert2" "~5.0.1" + "@abp/timeago" "~5.0.1" + "@abp/toastr" "~5.0.1" + +"@abp/aspnetcore.mvc.ui@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-5.0.1.tgz#f5aa4e0dcc10878f590e30836887f3f9861e91e3" + integrity sha512-bN15UcNhTqeo0RXlIYws7Jd8ZfSBsVM6Y5wiAqbq1Q3Lvs1uLmryhZpn6sHmKw2Z3Lnhec7VjnZN0FwNpzEqUw== dependencies: ansi-colors "^4.1.1" extend-object "^1.0.0" @@ -41,162 +41,162 @@ merge-stream "^2.0.0" micromatch "^4.0.2" -"@abp/bootstrap-datepicker@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-5.0.0.tgz#b830c5932596ecd0d58291b9f3c8e12aaaee6d2a" - integrity sha512-Xxad5FKwwDM7mCbgQaKkPwenGquAjWwy0En0Wbmp6L+rkrssTFMyeeF2UooK/I4DbvSwsY/GmxhscL68B0rJ0g== +"@abp/bootstrap-datepicker@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-5.0.1.tgz#52116a154e343d84c87257ad1ef10b54012f4c33" + integrity sha512-ZJWaZKzGzshBAfyBqaUScfWqeBVfWGbZIwIplF8VKtDGcyI8R54bqDsydhS88CCnolRz4+tfq3QY6yAXrUEKAw== dependencies: bootstrap-datepicker "^1.9.0" -"@abp/bootstrap@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-5.0.0.tgz#3f307f9ae23df3193451ce6d9496ae1ed0358fbd" - integrity sha512-Se4aAa6fmwrv/T0TR+4DgCBicPrw3Qz/ybs6lbqPXz4HNOS1c/dMRZowARWZie7AqoI7mctGo0DeBikNOxxndQ== +"@abp/bootstrap@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-5.0.1.tgz#d542a8fd7151e22eda1643e313a67f9892c50533" + integrity sha512-wyVAp6a4OIwlqK7AZozs7wpsBZgzG55EKeChBBfUXloJPuirQHxccFNE41aAYWfKur+wj25WD5NBnzFuKyNYiQ== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" bootstrap "^5.1.0" bootstrap-v4-rtl "4.6.0-1" -"@abp/clipboard@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-5.0.0.tgz#e5ddb36b61e85aba7a3c0583ce981add52d8b70f" - integrity sha512-XN8TRRnzWo/OiZewWCkbcbpwkgAuVbERx7SeiqA28zhQVl2GDm/AKx2wHlkJFRy6OWTzcgqdUDZ5O58aRJt6IA== +"@abp/clipboard@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-5.0.1.tgz#1730d4faa7c2fd218369d7963d15c8c6df49f588" + integrity sha512-e6QMRKk4okl0UW6RbUqGF0kPc5tbpZZVYw0RQm3gpZm3W4BZ6ZEU5DDoirEBX6Rjemx/yle8RaBBXXSjmD+/VA== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" clipboard "^2.0.6" -"@abp/core@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-5.0.0.tgz#ef8097bf581ecc0f3040ab448d55c4bd60002211" - integrity sha512-C5E33KS39xI9nYFwKlK2TuON5j8SX5CnBYuV9ijiMhBKjCz/8+9GYXbsZnS6wR+YwgDoLlAAntBT2o5PkmditA== +"@abp/core@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-5.0.1.tgz#904fa2c22bcbf42deb56abf772c3a53362807a2f" + integrity sha512-QzX35Oa+Vncu2OP1tXvGjAVFznmD1tZRn4tlDQgn36B2MU06UEO0JSx7JVedPvhzQZceUe5+WWMfuYNM2xeBTw== dependencies: - "@abp/utils" "~5.0.0" + "@abp/utils" "~5.0.1" -"@abp/datatables.net-bs5@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-5.0.0.tgz#1ad946ebaf96ec9847c743dac072cf9cb141830c" - integrity sha512-7J4brA7ocIA8Eytwie9iyDTHB19H3TZNpLejs0jFNnD2Uwf6ESL/SvojeNDmfDJZ+a9v5COTcXmt+as3CNxHng== +"@abp/datatables.net-bs5@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-5.0.1.tgz#b8afa2255e374c86c50e57bd57c2b36a86d6a890" + integrity sha512-1fuCk+K4xW3XnDFo2+kPXHMoOTol4HLRcWVRELjoBQ5YbTxEPsbrhfNUeUsECDAmkxoqY7G3MEdtIVwmy5NPpg== dependencies: - "@abp/datatables.net" "~5.0.0" + "@abp/datatables.net" "~5.0.1" datatables.net-bs5 "^1.10.25" -"@abp/datatables.net@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-5.0.0.tgz#0d319a7b1e902d824219fd8f8d05d62c5cd1ee50" - integrity sha512-FUQgmfbSNxWnUwBbkhc5ZJkIsnqeVjpzzObllrSwlj7TNhp79KA6sa5KFp7D8RVYFp5aVvPFRUO4mZksyjXByg== +"@abp/datatables.net@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-5.0.1.tgz#0ce5bdf7aa5bf224b93a22758a016c50562d6569" + integrity sha512-x9D2YNtnOJRrKXi2Pwrqzz9sIOrn3o6x2c8ZxjyC54vh+SLQtLycAzQw7OyaZGdbFkudlJiCuME/FiuZvht8Og== dependencies: - "@abp/jquery" "~5.0.0" + "@abp/jquery" "~5.0.1" datatables.net "^1.10.21" -"@abp/font-awesome@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-5.0.0.tgz#a7c92726e255745324fa8e691ec406a6b76542e0" - integrity sha512-DQQr/+7PrnxmEZAc30Hdt7G8y8MT3y9V0xRoIfIMyjk1Dwv/7kO6xN/eXu4pxPC1BSGGj5nfMgEYxl6K/+4C6w== +"@abp/font-awesome@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-5.0.1.tgz#661753403499925661f880c53214841dd34f5210" + integrity sha512-C+alReNvHZzST55ITBTyFqhdTehPo5ondzfqc9QlyB3Kwk1DIq0CaLqfS2xU5glYGDSqKi7xfkyQnJdwu2hKRA== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" "@fortawesome/fontawesome-free" "^5.13.0" -"@abp/jquery-form@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-5.0.0.tgz#f24e3e7191c2a7221352ee916cebf8d7f1e50ace" - integrity sha512-N8u+JRDrwMkvdZrQE8c/JaOMFFFvxuF6GnTyXARxEFFuzB7PnYPj2s+kFvyFMFRQgFQjfwUU90H0WBNsDW5u9A== +"@abp/jquery-form@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-5.0.1.tgz#20fa51946f68a7acd6ef3dd3d8d1349307f681b4" + integrity sha512-Q7GG0+XFXs7pDoa4p3r7vm8+sUh5ohwovedPNVghtX8Rm2I1sfVfJKamPr24PCAyJcFVntU1WCt/CngneES3pw== dependencies: - "@abp/jquery" "~5.0.0" + "@abp/jquery" "~5.0.1" jquery-form "^4.3.0" -"@abp/jquery-validation-unobtrusive@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-5.0.0.tgz#65c618be30983884f3d8d6de1cf3cf39dbd98a6a" - integrity sha512-i7X+Zz4upwJYEczSWlZS7Zxt+3HeHBGVjAcO0Tk3/oefd6qXpK2MIsIqy6icAutSM0oMsN0uLsJwuEx8vRqfyg== +"@abp/jquery-validation-unobtrusive@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-5.0.1.tgz#50798d3c087d0b103944b3a9e7c39a1f51db9fdc" + integrity sha512-w5SAYB0EZRbOhIFcCUXWvoGB/dWj3zyQOarbvzJ9LelgrXvvoissaaSSqq6BySjr1UigIWBCVuruAQssizmXBQ== dependencies: - "@abp/jquery-validation" "~5.0.0" + "@abp/jquery-validation" "~5.0.1" jquery-validation-unobtrusive "^3.2.11" -"@abp/jquery-validation@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-5.0.0.tgz#b28e7951d322babf46178e8007caa702d6c058a7" - integrity sha512-UI/P+NHBtYbJbSTDAyL84F9Pt/yy89NZVF4TZO9nspcn1Q9CbvC8AjETrHnp4m8n+HEckiuSMZNgkEy6j1hSwQ== +"@abp/jquery-validation@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-5.0.1.tgz#5932060d1fdf04a81e998caac959188aefdf1669" + integrity sha512-T5p0U2cWh6Jov55AiUxJYRw92L6ubFXfqL0EkaePV5AnoetVXheluhq9gQAKnwVAqtc/3Rxkr/lO047catUkjw== dependencies: - "@abp/jquery" "~5.0.0" + "@abp/jquery" "~5.0.1" jquery-validation "^1.19.2" -"@abp/jquery@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-5.0.0.tgz#8f61c41fa199a59f49f4a1e149c105976976ca93" - integrity sha512-h9TZRQGnOb4ZwKn80LCJRNRKeTETjGNm6zv25mvz+/IGbC+YMybe5xRnzZkgIXQKsd7zC0DumYOj3VENUuo8RA== +"@abp/jquery@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-5.0.1.tgz#1522ba1e0c488bffdc30a230ab35e225ae32d028" + integrity sha512-AacK4cy/RpzBfRYf7Jjn+TmVm72TW0jEjRoQ3TOXmlOrunRph05mQm72Ux2o8+Z1lOwxJKRz5gKSbRTfa6Tgpw== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" jquery "~3.6.0" -"@abp/lodash@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-5.0.0.tgz#bef626cd13e4c18fb24f0323a4fccc018ea91d6b" - integrity sha512-vFub55GnexnGqVuea7CXjJkPWXsEYRIKds195vmgDVjqKwNJlWAQENtuxRT7PiYIfywJWxJdokqZET2ZEsixTg== +"@abp/lodash@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-5.0.1.tgz#ad22895772ce65e52941dddb84d0a7d26c521d59" + integrity sha512-r1QqZCXfDd0YQnE4MkcY291H5jWFdZtGeYmwjIkn7aeOZ0PP9qsDPkWTwPwbpfmnz4DhmIdO5a1rveWz/J5ZwQ== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" lodash "^4.17.15" -"@abp/luxon@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-5.0.0.tgz#3dbaa82fbf73955839fb58dc93ad850786a58133" - integrity sha512-VilSmjNzFlcvYZHL+6VSfOnZPrGKA9TFvcxzaGRMG4sTLtT+1Ag0TMKF6aCe0Dslf6D1QwRDlMbikMXmHR99gA== +"@abp/luxon@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-5.0.1.tgz#7930e30db60e63b486a51030c60e09b54bcb3867" + integrity sha512-cXbnyAI9Gn2bZBQuCN46UFOkzb+djA3LMgpeMlTTv0aA87PrjzT60EPQ4a9FLovESqNnblaQ9Je43QwoGH8kZA== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" luxon "^1.24.1" -"@abp/malihu-custom-scrollbar-plugin@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-5.0.0.tgz#29eeb3a701195ae511ec7334940dba42ec206d69" - integrity sha512-g2mYagJwq92qA+PlIgqPD6PMJrznHgSM+Ixi/1VnVAKr+G6rNReo3wzrDOrkohXeenFsjRHIPnP/BOJw2mVwvg== +"@abp/malihu-custom-scrollbar-plugin@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-5.0.1.tgz#268ec30eef8ba72a0c8cb997e55d48a1508b2719" + integrity sha512-mqF6OizoWp6kg5pztNfF78+mbfyX3JAXDfv+ZUCvjtAqllIzyOUIqRSXULDkAcDeCC1+PluRsd+uSltIppC5hA== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/prismjs@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-5.0.0.tgz#1df5cbf13c83b02cb96306f95c8548c888bef600" - integrity sha512-csYxhDJeKxOljBcKUBic9ibwv3srDWbSsLfclFyW3sV3vfEDJ/gU3p3M1hr29PeESKtKxl+z1pK7iNz+gcUPFQ== +"@abp/prismjs@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-5.0.1.tgz#5154eeaeffed5c0cde6cfbf9891cc78ff6d5e309" + integrity sha512-JUrZleJPQBT0RHk3nRcNkEEib98p3l0Vhr8Ip4zrFbbx4uUNCkucHrZb1sBodPjWVLhi+BI2z7bMrkUe4eSiHw== dependencies: - "@abp/clipboard" "~5.0.0" - "@abp/core" "~5.0.0" + "@abp/clipboard" "~5.0.1" + "@abp/core" "~5.0.1" prismjs "^1.20.0" -"@abp/select2@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-5.0.0.tgz#dd56d841807a9c989a1dbc8bbb8279edd3ec8106" - integrity sha512-u2ggKeYLvUWYRVVNUeoaB/R1YAqLXMj6TfcdidWxrhLFvpjkerCRx2dholy0cAwAyAWssR4LCF/pANNcN7YTbg== +"@abp/select2@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-5.0.1.tgz#644c46c7c942a6b9d36bc76c7132f32ad577933b" + integrity sha512-FHVqEebQS3XiYn5TMEhGaQJOAYnPqxivKpeOB0F0F2bEDGqt3OXh05KcQBOm+sDSScq3c7T7HLr9mMzlYtyHXw== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" select2 "^4.0.13" -"@abp/sweetalert2@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-5.0.0.tgz#54ef957bc73c990336376e0a1978c5b1e857fcb7" - integrity sha512-yF3sddxZkhwVxjdMUgZsWpMkgei9VKVRuNm363Cz61mmtznx/c2Y243GNGkA5cwcAr+Q0pZkONlngAfDm2ifkw== +"@abp/sweetalert2@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-5.0.1.tgz#acf2acfc04619c7907237238bfaeb63f2d75fb22" + integrity sha512-CPqXIaHK7Ff6i8gNhxoZ9r3te9LIEiDCUpdHNtgRVMCaLbywfkiLyirlfRIB6lxRWSw8BZ+M/azdp5uzidW8Vw== dependencies: - "@abp/core" "~5.0.0" + "@abp/core" "~5.0.1" sweetalert2 "^11.0.18" -"@abp/timeago@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-5.0.0.tgz#e0d71d213d1dc64850d77e80ab13c6964bc8d03b" - integrity sha512-rpBmVKQ7fZeqV29Ls/+oIgshNqnj2qZySbNsx193ybruCr7JCiRgTXJ+Vig+Zox+3nTAZv8uDBN5VDZn7aSXvA== +"@abp/timeago@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-5.0.1.tgz#09ca6302304de09baa5ac8c72b950515ab0d0451" + integrity sha512-6onXho8R6eiWfncONZFBn18OwgQPKIaRM73rVevpRGJTf2pylcHtCp4uHGsoqgCc+TQU7FyoKTTkg4lfvCQa0A== dependencies: - "@abp/jquery" "~5.0.0" + "@abp/jquery" "~5.0.1" timeago "^1.6.7" -"@abp/toastr@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-5.0.0.tgz#b413d79b47cab2a4f8f72b98128ca8f22436c36a" - integrity sha512-YZSboTPXzQjZ1hR18VWIvMN+H8Q0J3DMrCtn6auGJ2fEJGcTXWjQViZc6XEKvat6Ms96tDCuQVf0Z3OfeLdX3Q== +"@abp/toastr@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-5.0.1.tgz#a3959df1de0c4640fabf508da9ccd3ed37ae2b03" + integrity sha512-ndL1uj2ce4PbGDzxh+QCIWYu+GAmW1Xwma4aJcq5zKo+xhlLJJnTe6QFGKr7wSsTVZMB7z1Pp2olvXH6an82+g== dependencies: - "@abp/jquery" "~5.0.0" + "@abp/jquery" "~5.0.1" toastr "^2.1.4" -"@abp/utils@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-5.0.0.tgz#2297601a8aff931c6eb2d62fac6a9756312e4ad8" - integrity sha512-dSbGhJE+Rs/V7VCdWgOV/6y59eXkbKMBaF88bvdDSR0QEwVpH0Fd18lu3gQhw/ENEPyd6kw9zqqnesToNw8lXg== +"@abp/utils@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-5.0.1.tgz#57934383004a21172bf7e08130813c08946894fb" + integrity sha512-WLCWe7iJ8krxsz8Z7E+8GDaGZuG/6PZDdqPcMIh95opi0kvcLxb+mjPXXpiXdtFzW7O09a+NaNOxorsEFxjoKw== dependencies: just-compare "^1.3.0" diff --git a/modules/blogging/app/Volo.BloggingTestApp/package.json b/modules/blogging/app/Volo.BloggingTestApp/package.json index b5542f65d3..a653b2dff4 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/package.json +++ b/modules/blogging/app/Volo.BloggingTestApp/package.json @@ -3,7 +3,7 @@ "name": "volo.blogtestapp", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "^5.0.0", - "@abp/blogging": "^5.0.0" + "@abp/aspnetcore.mvc.ui.theme.basic": "^5.0.1", + "@abp/blogging": "^5.0.1" } } \ No newline at end of file diff --git a/modules/blogging/app/Volo.BloggingTestApp/wwwroot/libs/tui-editor/toastui-editor-all.min.js b/modules/blogging/app/Volo.BloggingTestApp/wwwroot/libs/tui-editor/toastui-editor-all.min.js index 72d2ce7815..9803b8d7a0 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/wwwroot/libs/tui-editor/toastui-editor-all.min.js +++ b/modules/blogging/app/Volo.BloggingTestApp/wwwroot/libs/tui-editor/toastui-editor-all.min.js @@ -1,12 +1,12 @@ /*! * @toast-ui/editor - * @version 3.1.1 | Wed Oct 27 2021 + * @version 3.1.2 | Mon Dec 27 2021 * @author NHN FE Development Lab * @license MIT */ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.toastui=e():(t.toastui=t.toastui||{},t.toastui.Editor=e())}(self,(function(){return function(){var t={368:function(t){ /*! @license DOMPurify 2.3.3 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.3/LICENSE */ -t.exports=function(){"use strict";function t(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);e1?n-1:0),o=1;o/gm),j=a(/^data-[\-\w.\u00B7-\uFFFF]/),_=a(/^aria-[\-\w]+$/),q=a(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),V=a(/^(?:\w+script|data):/i),$=a(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),U="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};function W(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);e0&&void 0!==arguments[0]?arguments[0]:J(),e=function(t){return K(t)};if(e.version="2.3.3",e.removed=[],!t||!t.document||9!==t.document.nodeType)return e.isSupported=!1,e;var n=t.document,r=t.document,o=t.DocumentFragment,i=t.HTMLTemplateElement,a=t.Node,c=t.Element,l=t.NodeFilter,u=t.NamedNodeMap,p=void 0===u?t.NamedNodeMap||t.MozNamedAttrMap:u,x=t.Text,C=t.Comment,Z=t.DOMParser,X=t.trustedTypes,Q=c.prototype,Y=S(Q,"cloneNode"),tt=S(Q,"nextSibling"),et=S(Q,"childNodes"),nt=S(Q,"parentNode");if("function"==typeof i){var rt=r.createElement("template");rt.content&&rt.content.ownerDocument&&(r=rt.content.ownerDocument)}var ot=G(X,n),it=ot&&Ft?ot.createHTML(""):"",st=r,at=st.implementation,ct=st.createNodeIterator,lt=st.createDocumentFragment,ut=st.getElementsByTagName,pt=n.importNode,dt={};try{dt=M(r).documentMode?r.documentMode:{}}catch(t){}var ft={};e.isSupported="function"==typeof nt&&at&&void 0!==at.createHTMLDocument&&9!==dt;var ht=H,mt=z,vt=j,gt=_,yt=V,bt=$,wt=q,kt=null,xt=T({},[].concat(W(E),W(N),W(O),W(A),W(I))),Ct=null,Tt=T({},[].concat(W(R),W(P),W(B),W(F))),Mt=null,St=null,Et=!0,Nt=!0,Ot=!1,Dt=!1,At=!1,Lt=!1,It=!1,Rt=!1,Pt=!1,Bt=!0,Ft=!1,Ht=!0,zt=!0,jt=!1,_t={},qt=null,Vt=T({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),$t=null,Ut=T({},["audio","video","img","source","image","track"]),Wt=null,Jt=T({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Gt="http://www.w3.org/1998/Math/MathML",Kt="http://www.w3.org/2000/svg",Zt="http://www.w3.org/1999/xhtml",Xt=Zt,Qt=!1,Yt=void 0,te=["application/xhtml+xml","text/html"],ee="text/html",ne=void 0,re=null,oe=r.createElement("form"),ie=function(t){re&&re===t||(t&&"object"===(void 0===t?"undefined":U(t))||(t={}),t=M(t),kt="ALLOWED_TAGS"in t?T({},t.ALLOWED_TAGS):xt,Ct="ALLOWED_ATTR"in t?T({},t.ALLOWED_ATTR):Tt,Wt="ADD_URI_SAFE_ATTR"in t?T(M(Jt),t.ADD_URI_SAFE_ATTR):Jt,$t="ADD_DATA_URI_TAGS"in t?T(M(Ut),t.ADD_DATA_URI_TAGS):Ut,qt="FORBID_CONTENTS"in t?T({},t.FORBID_CONTENTS):Vt,Mt="FORBID_TAGS"in t?T({},t.FORBID_TAGS):{},St="FORBID_ATTR"in t?T({},t.FORBID_ATTR):{},_t="USE_PROFILES"in t&&t.USE_PROFILES,Et=!1!==t.ALLOW_ARIA_ATTR,Nt=!1!==t.ALLOW_DATA_ATTR,Ot=t.ALLOW_UNKNOWN_PROTOCOLS||!1,Dt=t.SAFE_FOR_TEMPLATES||!1,At=t.WHOLE_DOCUMENT||!1,Rt=t.RETURN_DOM||!1,Pt=t.RETURN_DOM_FRAGMENT||!1,Bt=!1!==t.RETURN_DOM_IMPORT,Ft=t.RETURN_TRUSTED_TYPE||!1,It=t.FORCE_BODY||!1,Ht=!1!==t.SANITIZE_DOM,zt=!1!==t.KEEP_CONTENT,jt=t.IN_PLACE||!1,wt=t.ALLOWED_URI_REGEXP||wt,Xt=t.NAMESPACE||Zt,Yt=Yt=-1===te.indexOf(t.PARSER_MEDIA_TYPE)?ee:t.PARSER_MEDIA_TYPE,ne="application/xhtml+xml"===Yt?function(t){return t}:m,Dt&&(Nt=!1),Pt&&(Rt=!0),_t&&(kt=T({},[].concat(W(I))),Ct=[],!0===_t.html&&(T(kt,E),T(Ct,R)),!0===_t.svg&&(T(kt,N),T(Ct,P),T(Ct,F)),!0===_t.svgFilters&&(T(kt,O),T(Ct,P),T(Ct,F)),!0===_t.mathMl&&(T(kt,A),T(Ct,B),T(Ct,F))),t.ADD_TAGS&&(kt===xt&&(kt=M(kt)),T(kt,t.ADD_TAGS)),t.ADD_ATTR&&(Ct===Tt&&(Ct=M(Ct)),T(Ct,t.ADD_ATTR)),t.ADD_URI_SAFE_ATTR&&T(Wt,t.ADD_URI_SAFE_ATTR),t.FORBID_CONTENTS&&(qt===Vt&&(qt=M(qt)),T(qt,t.FORBID_CONTENTS)),zt&&(kt["#text"]=!0),At&&T(kt,["html","head","body"]),kt.table&&(T(kt,["tbody"]),delete Mt.tbody),s&&s(t),re=t)},se=T({},["mi","mo","mn","ms","mtext"]),ae=T({},["foreignobject","desc","title","annotation-xml"]),ce=T({},N);T(ce,O),T(ce,D);var le=T({},A);T(le,L);var ue=function(t){var e=nt(t);e&&e.tagName||(e={namespaceURI:Zt,tagName:"template"});var n=m(t.tagName),r=m(e.tagName);if(t.namespaceURI===Kt)return e.namespaceURI===Zt?"svg"===n:e.namespaceURI===Gt?"svg"===n&&("annotation-xml"===r||se[r]):Boolean(ce[n]);if(t.namespaceURI===Gt)return e.namespaceURI===Zt?"math"===n:e.namespaceURI===Kt?"math"===n&&ae[r]:Boolean(le[n]);if(t.namespaceURI===Zt){if(e.namespaceURI===Kt&&!ae[r])return!1;if(e.namespaceURI===Gt&&!se[r])return!1;var o=T({},["title","style","font","a","script"]);return!le[n]&&(o[n]||!ce[n])}return!1},pe=function(t){h(e.removed,{element:t});try{t.parentNode.removeChild(t)}catch(e){try{t.outerHTML=it}catch(e){t.remove()}}},de=function(t,n){try{h(e.removed,{attribute:n.getAttributeNode(t),from:n})}catch(t){h(e.removed,{attribute:null,from:n})}if(n.removeAttribute(t),"is"===t&&!Ct[t])if(Rt||Pt)try{pe(n)}catch(t){}else try{n.setAttribute(t,"")}catch(t){}},fe=function(t){var e=void 0,n=void 0;if(It)t=""+t;else{var o=v(t,/^[\r\n\t ]+/);n=o&&o[0]}"application/xhtml+xml"===Yt&&(t=''+t+"");var i=ot?ot.createHTML(t):t;if(Xt===Zt)try{e=(new Z).parseFromString(i,Yt)}catch(t){}if(!e||!e.documentElement){e=at.createDocument(Xt,"template",null);try{e.documentElement.innerHTML=Qt?"":i}catch(t){}}var s=e.body||e.documentElement;return t&&n&&s.insertBefore(r.createTextNode(n),s.childNodes[0]||null),Xt===Zt?ut.call(e,At?"html":"body")[0]:At?e.documentElement:s},he=function(t){return ct.call(t.ownerDocument||t,t,l.SHOW_ELEMENT|l.SHOW_COMMENT|l.SHOW_TEXT,null,!1)},me=function(t){return!(t instanceof x||t instanceof C||"string"==typeof t.nodeName&&"string"==typeof t.textContent&&"function"==typeof t.removeChild&&t.attributes instanceof p&&"function"==typeof t.removeAttribute&&"function"==typeof t.setAttribute&&"string"==typeof t.namespaceURI&&"function"==typeof t.insertBefore)},ve=function(t){return"object"===(void 0===a?"undefined":U(a))?t instanceof a:t&&"object"===(void 0===t?"undefined":U(t))&&"number"==typeof t.nodeType&&"string"==typeof t.nodeName},ge=function(t,n,r){ft[t]&&d(ft[t],(function(t){t.call(e,n,r,re)}))},ye=function(t){var n=void 0;if(ge("beforeSanitizeElements",t,null),me(t))return pe(t),!0;if(v(t.nodeName,/[\u0080-\uFFFF]/))return pe(t),!0;var r=ne(t.nodeName);if(ge("uponSanitizeElement",t,{tagName:r,allowedTags:kt}),!ve(t.firstElementChild)&&(!ve(t.content)||!ve(t.content.firstElementChild))&&w(/<[/\w]/g,t.innerHTML)&&w(/<[/\w]/g,t.textContent))return pe(t),!0;if("select"===r&&w(/