Browse Source

Merge branch 'dev' into Using-ABP-CLI-with-local-paths

pull/1857/head
Yunus Emre Kalkan 7 years ago
parent
commit
db2c848c18
  1. 3
      docs/en/AspNetCore/JavaScript-API/Auth.md
  2. 24
      docs/en/AspNetCore/JavaScript-API/Index.md
  3. 370
      docs/en/Authorization.md
  4. 3
      docs/en/Modules/Permission-Management.md
  5. 3
      docs/en/docs-nav.json
  6. BIN
      docs/en/images/authorization-new-permission-ui-hierarcy.png
  7. BIN
      docs/en/images/authorization-new-permission-ui-localized.png
  8. BIN
      docs/en/images/authorization-new-permission-ui.png
  9. 9
      framework/Volo.Abp.sln
  10. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/AbpAspNetCoreMvcUiMultiTenancyModule.cs
  11. 11
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/AbpAspNetCoreMvcUIBasicThemeModule.cs
  12. 11
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/AbpAspNetCoreMvcUiThemeSharedModule.cs
  13. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Error/Index.cshtml.cs
  14. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpAspNetCoreMvcUiWidgetsModule.cs
  15. 11
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/AbpAspNetCoreMvcUiModule.cs
  16. 20
      framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpMvcBuilderExtensions.cs
  17. 6
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
  18. 21
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs
  19. 1
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs
  20. 2
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs
  21. 3
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs
  22. 2
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs
  23. 3
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultHttpExceptionStatusCodeFinder.cs
  24. 2
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/ExceptionHttpStatusCodeOptions.cs
  25. 2
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/IExceptionToErrorInfoConverter.cs
  26. 2
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/IHttpExceptionStatusCodeFinder.cs
  27. 10
      framework/src/Volo.Abp.Authorization/Microsoft/Extensions/DependencyInjection/AbpAuthorizationServiceCollectionExtensions.cs
  28. 20
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo.Abp.Ddd.Application.Contracts.csproj
  29. 12
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/AbpDddApplicationContractsModule.cs
  30. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/AuditedEntityDto.cs
  31. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/AuditedEntityWithUserDto.cs
  32. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/CreationAuditedEntityDto.cs
  33. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/CreationAuditedEntityWithUserDto.cs
  34. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/EntityDto.cs
  35. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/FullAuditedEntityDto.cs
  36. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/FullAuditedEntityWithUserDto.cs
  37. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/IEntityDto.cs
  38. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/IHasTotalCount.cs
  39. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ILimitedResultRequest.cs
  40. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/IListResult.cs
  41. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/IPagedAndSortedResultRequest.cs
  42. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/IPagedResult.cs
  43. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/IPagedResultRequest.cs
  44. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ISortedResultRequest.cs
  45. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/LimitedResultRequestDto.cs
  46. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ListResultDto.cs
  47. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/PagedAndSortedResultRequestDto.cs
  48. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/PagedResultDto.cs
  49. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/PagedResultRequestDto.cs
  50. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IApplicationService.cs
  51. 0
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICrudAppService.cs
  52. 4
      framework/src/Volo.Abp.Ddd.Application/Volo.Abp.Ddd.Application.csproj
  53. 2
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs
  54. 10
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  55. 4
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs
  56. 8
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs
  57. 11
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs
  58. 2
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs
  59. 7
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs
  60. 2
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml
  61. 2
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs
  62. 1
      npm/ng-packs/.gitignore
  63. 25
      npm/ng-packs/angular.json
  64. 5
      npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts
  65. 18
      npm/ng-packs/apps/dev-app/src/app/app.component.ts
  66. 2
      npm/ng-packs/apps/dev-app/src/app/app.module.ts
  67. 7
      npm/ng-packs/apps/dev-app/src/app/lazy-libs/setting-management-wrapper.module.ts
  68. 4
      npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts
  69. 2
      npm/ng-packs/packages/account/ng-package.json
  70. 3
      npm/ng-packs/packages/account/package.json
  71. 6
      npm/ng-packs/packages/account/src/lib/services/account.service.ts
  72. 3
      npm/ng-packs/packages/core/src/lib/core.module.ts
  73. 12
      npm/ng-packs/packages/core/src/lib/directives/sort.directive.ts
  74. 14
      npm/ng-packs/packages/core/src/lib/models/application-configuration.ts
  75. 26
      npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts
  76. 17
      npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts
  77. 86
      npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts
  78. 36
      npm/ng-packs/packages/core/src/lib/states/config.state.ts
  79. 6
      npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts
  80. 14
      npm/ng-packs/packages/core/src/lib/utils/route-utils.ts
  81. 1
      npm/ng-packs/packages/core/src/public-api.ts
  82. 6
      npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts
  83. 13
      npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html
  84. 22
      npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts
  85. 28
      npm/ng-packs/packages/identity/src/lib/components/users/users.component.html
  86. 36
      npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts
  87. 19
      npm/ng-packs/packages/setting-management-config/src/lib/services/setting-management-config.service.ts
  88. 8
      npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.ts
  89. 4
      npm/ng-packs/packages/tenant-management-config/src/lib/services/tenant-management-config.service.ts
  90. 13
      npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html
  91. 34
      npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts
  92. 2
      npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.html
  93. 9
      npm/ng-packs/packages/theme-shared/src/lib/animations/slide.animations.ts
  94. 9
      npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts
  95. 1
      npm/ng-packs/packages/theme-shared/src/lib/components/index.ts
  96. 3
      npm/ng-packs/packages/theme-shared/src/lib/components/sort-order-icon/sort-order-icon.component.html
  97. 61
      npm/ng-packs/packages/theme-shared/src/lib/components/sort-order-icon/sort-order-icon.component.ts
  98. 2
      npm/ng-packs/packages/theme-shared/src/lib/contants/styles.ts
  99. 14
      npm/ng-packs/packages/theme-shared/src/lib/models/setting-management.ts
  100. 21
      npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts

3
docs/en/AspNetCore/JavaScript-API/Auth.md

@ -0,0 +1,3 @@
# abp.auth JavaScript API
TODO

24
docs/en/AspNetCore/JavaScript-API/Index.md

@ -0,0 +1,24 @@
# JavaScript API
ABP provides some JavaScript APIs for ASP.NET Core MVC / Razor Pages applications. They can be used to perform some common application requirements in the client side.
## APIs
* abp.ajax
* [abp.auth](Auth.md)
* abp.currentUser
* abp.dom
* abp.event
* abp.features
* abp.localization
* abp.log
* abp.ModalManager
* abp.notify
* abp.security
* abp.setting
* abp.ui
* abp.utils
* abp.ResourceLoader
* abp.WidgetManager
* Other APIs

370
docs/en/Authorization.md

@ -1,3 +1,369 @@
## Authorization
# Authorization
TODO
Authorization is used to check if a user is allowed to perform some specific operations in the application.
ABP extends [ASP.NET Core Authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction) by adding **permissions** as auto [policies](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies) and allowing authorization system to be usable in the **[application services](Application-Services.md)** too.
So, all the ASP.NET Core authorization features and the documentation are valid in an ABP based application. This document focuses on the features that added on top of ASP.NET Core authorization features.
## Authorize Attribute
ASP.NET Core defines the [**Authorize**](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/simple) attribute that can be used for an action, a controller or a page. ABP allows you to use the same attribute for an [application service](Application-Services.md) too.
Example:
````csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Services;
namespace Acme.BookStore
{
[Authorize]
public class AuthorAppService : ApplicationService, IAuthorAppService
{
public Task<List<AuthorDto>> GetListAsync()
{
...
}
[AllowAnonymous]
public Task<AuthorDto> GetAsync(Guid id)
{
...
}
[Authorize("BookStore_Author_Create")]
public Task CreateAsync(CreateAuthorDto input)
{
...
}
}
}
````
* `Authorize` attribute forces the user to login into the application in order to use the `AuthorAppService` methods. So, `GetListAsync` method is only available to the authenticated users.
* `AllowAnonymous` suppresses the authentication. So, `GetAsync` method is available to everyone including unauthorized users.
* `[Authorize("BookStore_Author_Create")]` defines a policy (see [policy based authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies)) that is checked to authorize the current user.
"BookStore_Author_Create" is an arbitrary policy name. If you declare an attribute like that, ASP.NET Core authorization system expects a policy to be defined before.
You can, of course, implement your policies as stated in the ASP.NET Core documentation. But for simple true/false conditions like a policy was granted to a user or not, ABP defines the permission system which will be explained in the next section.
## Permission System
A permission is a simple policy that is granted or prohibited for a particular user, role or client.
### Defining Permissions
To define permissions, create a class inheriting from the `PermissionDefinitionProvider` as shown below:
````csharp
using Volo.Abp.Authorization.Permissions;
namespace Acme.BookStore.Permissions
{
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var myGroup = context.AddGroup("BookStore");
myGroup.AddPermission("BookStore_Author_Create");
}
}
}
````
> ABP automatically discovers this class. No additional configuration required!
In the `Define` method, you first need to add a **permission group** or get an existing group then add **permissions** to this group.
When you define a permission, it becomes usable in the ASP.NET Core authorization system as a **policy** name. It also becomes visible in the UI. See permissions dialog for a role:
![authorization-new-permission-ui](images/authorization-new-permission-ui.png)
* The "BookStore" group is shown as a new tab on the left side.
* "BookStore_Author_Create" on the right side is the permission name. You can grant or prohibit it for the role.
When you save the dialog, it is saved to the database and used in the authorization system.
> The screen above is available when you have installed the identity module, which is basically used for user and role management. Startup templates come with the identity module pre-installed.
#### Localizing the Permission Name
"BookStore_Author_Create" is not a good permission name for the UI. Fortunately, `AddPermission` and `AddGroup` methods can take `LocalizableString` as second parameters:
````csharp
var myGroup = context.AddGroup(
"BookStore",
LocalizableString.Create<BookStoreResource>("BookStore")
);
myGroup.AddPermission(
"BookStore_Author_Create",
LocalizableString.Create<BookStoreResource>("Permission:BookStore_Author_Create")
);
````
Then you can define texts for "BookStore" and "Permission:BookStore_Author_Create" keys in the localization file:
````json
"BookStore": "Book Store",
"Permission:BookStore_Author_Create": "Creating a new author"
````
> For more information, see the [localization document](Localization.md) on the localization system.
The localized UI will be as seen below:
![authorization-new-permission-ui-localized](images/authorization-new-permission-ui-localized.png)
#### Multi-Tenancy
ABP supports [multi-tenancy](Multi-Tenancy.md) as a first class citizen. You can define multi-tenancy side option while defining a new permission. It gets one of the three values defined below:
* **Host**: The permission is available only for the host side.
* **Tenant**: The permission is available only for the tenant side.
* **Both** (default): The permission is available both for tenant and host sides.
> If your application is not multi-tenant, you can ignore this option.
To set the multi-tenancy side option, pass to the third parameter of the `AddPermission` method:
````csharp
myGroup.AddPermission(
"BookStore_Author_Create",
LocalizableString.Create<BookStoreResource>("Permission:BookStore_Author_Create"),
multiTenancySide: MultiTenancySides.Tenant //set multi-tenancy side!
);
````
#### Child Permissions
A permission may have child permissions. It is especially useful when you want to create a hierarchical permission tree where a permission may have additional sub permissions which are available only if the parent permission has been granted.
Example definition:
````csharp
var authorManagement = myGroup.AddPermission("Author_Management");
authorManagement.AddChild("Author_Management_Create_Books");
authorManagement.AddChild("Author_Management_Edit_Books");
authorManagement.AddChild("Author_Management_Delete_Books");
````
The result on the UI is shown below (you probably want to localize permissions for your application):
![authorization-new-permission-ui-hierarcy](images/authorization-new-permission-ui-hierarcy.png)
For the example code, it is assumed that a role/user with "Author_Management" permission granted may have additional permissions. Then a typical application service that checks permissions can be defined as shown below:
````csharp
[Authorize("Author_Management")]
public class AuthorAppService : ApplicationService, IAuthorAppService
{
public Task<List<AuthorDto>> GetListAsync()
{
...
}
public Task<AuthorDto> GetAsync(Guid id)
{
...
}
[Authorize("Author_Management_Create_Books")]
public Task CreateAsync(CreateAuthorDto input)
{
...
}
[Authorize("Author_Management_Edit_Books")]
public Task UpdateAsync(CreateAuthorDto input)
{
...
}
[Authorize("Author_Management_Delete_Books")]
public Task DeleteAsync(CreateAuthorDto input)
{
...
}
}
````
* `GetListAsync` and `GetAsync` will be available to users if they have `Author_Management` permission is granted.
* Other methods require additional permissions.
### Overriding a Permission by a Custom Policy
If you define and register a policy to the ASP.NET Core authorization system with the same name of a permission, your policy will override the existing permission. This is a powerful way to extend the authorization for a pre-built module that you are using in your application.
See [policy based authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies) document to learn how to define a custom policy.
## IAuthorizationService
ASP.NET Core provides the `IAuthorizationService` that can be used to check for authorization. Once you inject, you can use it in your code to conditionally control the authorization.
Example:
````csharp
public async Task CreateAsync(CreateAuthorDto input)
{
var result = await AuthorizationService
.AuthorizeAsync("Author_Management_Create_Books");
if (result.Succeeded == false)
{
//throw exception
throw new AbpAuthorizationException("...");
}
//continue to the normal flow...
}
````
> `AuthorizationService` is available as a property when you derive from ABP's `ApplicationService` base class. Since it is widely used in application services, `ApplicationService` pre-injects it for you. Otherwise, you can directly [inject](Dependency-Injection.md) it into your class.
Since this is a typical code block, ABP provides extension methods to simplify it.
Example:
````csharp
public async Task CreateAsync(CreateAuthorDto input)
{
await AuthorizationService.CheckAsync("Author_Management_Create_Books");
//continue to the normal flow...
}
````
`CheckAsync` extension method throws `AbpAuthorizationException` if the current user/client is not granted for the given permission. There is also `IsGrantedAsync` extension method that returns `true` or `false`.
`IAuthorizationService` has some overloads for the `AuthorizeAsync` method. These are explained in the [ASP.NET Core authorization documentation](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction).
> Tip: Prefer to use the `Authorize` attribute wherever possible, since it is declarative & simple. Use `IAuthorizationService` if you need to conditionally check a permission and run a business code based on the permission check.
### Check a Permission in JavaScript
You may need to check a policy/permission on the client side. For ASP.NET Core MVC / Razor Pages applications, you can use the `abp.auth` API. Example:
````js
abp.auth.isGranted('MyPermissionName');
````
See [abp.auth](AspNetCore/JavaScript-API/Auth.md) API documentation for details.
## Permission Management
Permission management is normally done by an admin user using the permission management modal:
![authorization-new-permission-ui-localized](images/authorization-new-permission-ui-localized.png)
If you need to manage permissions by code, inject the `IPermissionManager` and use as shown below:
````csharp
public class MyService : ITransientDependency
{
private readonly IPermissionManager _permissionManager;
public MyService(IPermissionManager permissionManager)
{
_permissionManager = permissionManager;
}
public async Task GrantPermissionForUserAsync(Guid userId, string permissionName)
{
await _permissionManager.SetForUserAsync(userId, permissionName, true);
}
public async Task ProhibitPermissionForUserAsync(Guid userId, string permissionName)
{
await _permissionManager.SetForUserAsync(userId, permissionName, false);
}
}
````
`SetForUserAsync` sets the value (true/false) for a permission of a user. There are more extension methods like `SetForRoleAsync` and `SetForClientAsync`.
`IPermissionManager` is defined by the permission management module. See the [permission management module documentation](Modules/Permission-Management.md) for more information.
## Advanced Topics
### Permission Value Providers
Permission checking system is extensible. Any class derived from `PermissionValueProvider` (or implements `IPermissionValueProvider`) can contribute to the permission check. There are three pre-defined value providers:
* `UserPermissionValueProvider` checks if the current user is granted for the given permission. It gets user id from the current claims. User claim name is defined with the `AbpClaimTypes.UserId` static property.
* `RolePermissionValueProvider` checks if any of the roles of the current user is granted for the given permission. It gets role names from the current claims. Role claims name is defined with the `AbpClaimTypes.Role` static property.
* `ClientPermissionValueProvider` checks if the current client is granted for the given permission. This is especially useful on a machine to machine interaction where there is no current user. It gets the client id from the current claims. Client claim name is defined with the `AbpClaimTypes.ClientId` static property.
You can extend the permission checking system by defining your own permission value provider.
Example:
````csharp
public class SystemAdminPermissionValueProvider : PermissionValueProvider
{
public SystemAdminPermissionValueProvider(IPermissionStore permissionStore)
: base(permissionStore)
{
}
public override string Name => "SystemAdmin";
public override async Task<PermissionGrantResult>
CheckAsync(PermissionValueCheckContext context)
{
if (context.Principal?.FindFirst("User_Type")?.Value == "SystemAdmin")
{
return PermissionGrantResult.Granted;
}
return PermissionGrantResult.Undefined;
}
}
````
This provider allows for all permissions to a user with a `User_Type` claim that has `SystemAdmin` value. It is common to use current claims and `IPermissionStore` in a permission value provider.
A permission value provider should return one of the following values from the `CheckAsync` method:
* `PermissionGrantResult.Granted` is returned to grant the user for the permission. If any of the providers return `Granted`, the result will be `Granted`, if no other provider returns `Prohibited`.
* `PermissionGrantResult.Prohibited` is returned to prohibit the user for the permission. If any of the providers return `Prohibited`, the result will always be `Prohibited`. Doesn't matter what other providers return.
* `PermissionGrantResult.Undefined` is returned if this value provider could not decide about the permission value. Return this to let other providers check the permission.
Once a provider is defined, it should be added to the `PermissionOptions` as shown below:
````csharp
Configure<PermissionOptions>(options =>
{
options.ValueProviders.Add<SystemAdminPermissionValueProvider>();
});
````
### Permission Store
`IPermissionStore` is the only interface that needs to be implemented to read the value of permissions from a persistence source, generally a database system. Permission management module implements it. See the [permission management module documentation](Modules/Permission-Management.md) for more information
### AlwaysAllowAuthorizationService
`AlwaysAllowAuthorizationService` is a class that is used to bypass the authorization service. It is generally used in integration tests where you may want to disable the authorization system.
Use `IServiceCollection.AddAlwaysAllowAuthorization()` extension method to register the `AlwaysAllowAuthorizationService` to the [dependency injection](Dependency-Injection.md) system:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAlwaysAllowAuthorization();
}
````
This is already done for the startup template integration tests.
## See Also
* [Permission Management Module](Modules/Permission-Management.md)
* [ASP.NET Core MVC / Razor Pages JavaScript Auth API](AspNetCore/JavaScript-API/Auth.md)

3
docs/en/Modules/Permission-Management.md

@ -0,0 +1,3 @@
# Permission Management Module
TODO

3
docs/en/docs-nav.json

@ -82,7 +82,8 @@
"text": "Validation"
},
{
"text": "Authorization"
"text": "Authorization",
"path": "Authorization.md"
},
{
"text": "Caching"

BIN
docs/en/images/authorization-new-permission-ui-hierarcy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
docs/en/images/authorization-new-permission-ui-localized.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
docs/en/images/authorization-new-permission-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

9
framework/Volo.Abp.sln

@ -248,7 +248,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.MailKit", "src\Vol
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.MailKit.Tests", "test\Volo.Abp.MailKit.Tests\Volo.Abp.MailKit.Tests.csproj", "{70DD6E17-B98B-4B00-8F38-C489E291BB53}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.ObjectMapping.Tests", "test\Volo.Abp.ObjectMapping.Tests\Volo.Abp.ObjectMapping.Tests.csproj", "{667F5544-C1EB-447C-96FD-9B757F04DE2B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.ObjectMapping.Tests", "test\Volo.Abp.ObjectMapping.Tests\Volo.Abp.ObjectMapping.Tests.csproj", "{667F5544-C1EB-447C-96FD-9B757F04DE2B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Ddd.Application.Contracts", "src\Volo.Abp.Ddd.Application.Contracts\Volo.Abp.Ddd.Application.Contracts.csproj", "{73559227-EBF0-475F-835B-1FF0CD9132AA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -744,6 +746,10 @@ Global
{667F5544-C1EB-447C-96FD-9B757F04DE2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{667F5544-C1EB-447C-96FD-9B757F04DE2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{667F5544-C1EB-447C-96FD-9B757F04DE2B}.Release|Any CPU.Build.0 = Release|Any CPU
{73559227-EBF0-475F-835B-1FF0CD9132AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{73559227-EBF0-475F-835B-1FF0CD9132AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{73559227-EBF0-475F-835B-1FF0CD9132AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{73559227-EBF0-475F-835B-1FF0CD9132AA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -871,6 +877,7 @@ Global
{0CAED4CC-1CFD-4092-A326-AFE4DB3A9AB4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{70DD6E17-B98B-4B00-8F38-C489E291BB53} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{667F5544-C1EB-447C-96FD-9B757F04DE2B} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{73559227-EBF0-475F-835B-1FF0CD9132AA} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/AbpAspNetCoreMvcUiMultiTenancyModule.cs

@ -1,4 +1,5 @@
using Volo.Abp.AspNetCore.MultiTenancy;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.Localization;
@ -25,6 +26,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy
typeof(AbpAspNetCoreMvcUiMultiTenancyModule).Assembly
);
});
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAspNetCoreMvcUiMultiTenancyModule).Assembly);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)

11
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/AbpAspNetCoreMvcUIBasicThemeModule.cs

@ -1,4 +1,5 @@
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Toolbars;
@ -17,6 +18,14 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic
)]
public class AbpAspNetCoreMvcUiBasicThemeModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAspNetCoreMvcUiBasicThemeModule).Assembly);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<ThemingOptions>(options =>

11
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/AbpAspNetCoreMvcUiThemeSharedModule.cs

@ -1,4 +1,5 @@
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling;
@ -15,6 +16,14 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared
)]
public class AbpAspNetCoreMvcUiThemeSharedModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAspNetCoreMvcUiThemeSharedModule).Assembly);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<VirtualFileSystemOptions>(options =>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Error/Index.cshtml.cs

@ -2,7 +2,7 @@ using System;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Http;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Error

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpAspNetCoreMvcUiWidgetsModule.cs

@ -17,6 +17,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAspNetCoreMvcUiWidgetsModule).Assembly);
});
AutoAddWidgets(context.Services);
}

11
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/AbpAspNetCoreMvcUiModule.cs

@ -1,4 +1,5 @@
using Volo.Abp.Modularity;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.UI.Navigation;
using Volo.Abp.VirtualFileSystem;
@ -8,6 +9,14 @@ namespace Volo.Abp.AspNetCore.Mvc.UI
[DependsOn(typeof(AbpUiNavigationModule))]
public class AbpAspNetCoreMvcUiModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAspNetCoreMvcUiModule).Assembly);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<VirtualFileSystemOptions>(options =>

20
framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpMvcBuilderExtensions.cs

@ -0,0 +1,20 @@
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
namespace Microsoft.Extensions.DependencyInjection
{
public static class AbpMvcBuilderExtensions
{
public static void AddApplicationPartIfNotExists(this IMvcBuilder mvcBuilder, Assembly assembly)
{
if (mvcBuilder.PartManager.ApplicationParts.Any(
p => p is AssemblyPart assemblyPart && assemblyPart.Assembly == assembly))
{
return;
}
mvcBuilder.PartManager.ApplicationParts.Add(new AssemblyPart(assembly));
}
}
}

6
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs

@ -66,7 +66,7 @@ namespace Volo.Abp.AspNetCore.Mvc
var mvcCoreBuilder = context.Services.AddMvcCore();
context.Services.ExecutePreConfiguredActions(mvcCoreBuilder);
var abpMvcDataAnnotationsLocalizationOptions = context.Services.ExecutePreConfiguredActions(new AbpMvcDataAnnotationsLocalizationOptions());
context.Services
@ -85,7 +85,6 @@ namespace Volo.Abp.AspNetCore.Mvc
.AddRazorRuntimeCompilation()
.AddDataAnnotationsLocalization(options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) =>
{
var resourceType = abpMvcDataAnnotationsLocalizationOptions.AssemblyResources.GetOrDefault(type.Assembly);
@ -93,7 +92,7 @@ namespace Volo.Abp.AspNetCore.Mvc
};
})
.AddViewLocalization(); //TODO: How to configure from the application? Also, consider to move to a UI module since APIs does not care about it.
context.Services.ExecutePreConfiguredActions(mvcBuilder);
//TODO: AddViewLocalization by default..?
@ -111,6 +110,7 @@ namespace Volo.Abp.AspNetCore.Mvc
var application = context.Services.GetSingletonInstance<IAbpApplication>();
partManager.FeatureProviders.Add(new AbpConventionalControllerFeatureProvider(application));
partManager.ApplicationParts.Add(new AssemblyPart(typeof(AbpAspNetCoreMvcModule).Assembly));
Configure<MvcOptions>(mvcOptions =>
{

21
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs

@ -220,11 +220,26 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
{
foreach (var selector in action.Selectors)
{
var httpMethod = selector.ActionConstraints.OfType<HttpMethodActionConstraint>().FirstOrDefault()?.HttpMethods?.FirstOrDefault();
var httpMethod = selector.ActionConstraints
.OfType<HttpMethodActionConstraint>()
.FirstOrDefault()?
.HttpMethods?
.FirstOrDefault();
if (httpMethod == null)
{
httpMethod = SelectHttpMethod(action, configuration);
}
if (selector.AttributeRouteModel == null)
{
selector.AttributeRouteModel = CreateAbpServiceAttributeRouteModel(rootPath, controllerName, action, httpMethod, configuration);
}
if (!selector.ActionConstraints.OfType<HttpMethodActionConstraint>().Any())
{
selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] {httpMethod}));
}
}
}
@ -336,7 +351,9 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
protected virtual bool IsEmptySelector(SelectorModel selector)
{
return selector.AttributeRouteModel == null && selector.ActionConstraints.IsNullOrEmpty();
return selector.AttributeRouteModel == null
&& selector.ActionConstraints.IsNullOrEmpty()
&& selector.EndpointMetadata.IsNullOrEmpty();
}
protected virtual bool ImplementsRemoteServiceInterface(Type controllerType)

1
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http;
using Volo.Abp.Json;

2
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs

@ -4,7 +4,7 @@ using Microsoft.AspNetCore.RequestLocalization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.AspNetCore.Auditing;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.Tracing;
using Volo.Abp.AspNetCore.Uow;
using Volo.Abp.DependencyInjection;

3
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs

@ -4,12 +4,13 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Uow;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http;
using Volo.Abp.Json;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class AbpExceptionHandlingMiddleware : IMiddleware, ITransientDependency
{

2
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs

@ -15,7 +15,7 @@ using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Validation;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class DefaultExceptionToErrorInfoConverter : IExceptionToErrorInfoConverter, ITransientDependency
{

3
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultHttpExceptionStatusCodeFinder.cs

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
@ -9,7 +8,7 @@ using Volo.Abp.Domain.Entities;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Validation;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class DefaultHttpExceptionStatusCodeFinder : IHttpExceptionStatusCodeFinder, ITransientDependency
{

2
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/ExceptionHttpStatusCodeOptions.cs

@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Net;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class ExceptionHttpStatusCodeOptions
{

2
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/IExceptionToErrorInfoConverter.cs

@ -1,7 +1,7 @@
using System;
using Volo.Abp.Http;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
/// <summary>
/// This interface can be implemented to convert an <see cref="Exception"/> object to an <see cref="RemoteServiceErrorInfo"/> object.

2
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/IHttpExceptionStatusCodeFinder.cs

@ -2,7 +2,7 @@
using System.Net;
using Microsoft.AspNetCore.Http;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public interface IHttpExceptionStatusCodeFinder
{

10
framework/src/Volo.Abp.Authorization/Microsoft/Extensions/DependencyInjection/AbpAuthorizationServiceCollectionExtensions.cs

@ -1,5 +1,4 @@
using System;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Authorization;
using Volo.Abp.Authorization.Permissions;
@ -8,13 +7,6 @@ namespace Microsoft.Extensions.DependencyInjection
{
public static class AbpAuthorizationServiceCollectionExtensions
{
//TODO: Remove this and use AddAlwaysAllowAuthorization
[Obsolete("Use AddAlwaysAllowAuthorization instead")]
public static IServiceCollection AddAlwaysAllowPermissionChecker(this IServiceCollection services)
{
return services.Replace(ServiceDescriptor.Singleton<IPermissionChecker, AlwaysAllowPermissionChecker>());
}
public static IServiceCollection AddAlwaysAllowAuthorization(this IServiceCollection services)
{
services.Replace(ServiceDescriptor.Singleton<IAuthorizationService, AlwaysAllowAuthorizationService>());

20
framework/src/Volo.Abp.Ddd.Application.Contracts/Volo.Abp.Ddd.Application.Contracts.csproj

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.Ddd.Application.Contracts</AssemblyName>
<PackageId>Volo.Abp.Ddd.Application.Contracts</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Auditing\Volo.Abp.Auditing.csproj" />
</ItemGroup>
</Project>

12
framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/AbpDddApplicationContractsModule.cs

@ -0,0 +1,12 @@
using Volo.Abp.Auditing;
using Volo.Abp.Modularity;
namespace Volo.Abp.Application
{
[DependsOn(
typeof(AbpAuditingModule)
)]
public class AbpDddApplicationContractsModule : AbpModule
{
}
}

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/AuditedEntityDto.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/AuditedEntityDto.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/AuditedEntityWithUserDto.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/AuditedEntityWithUserDto.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/CreationAuditedEntityDto.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/CreationAuditedEntityDto.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/CreationAuditedEntityWithUserDto.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/CreationAuditedEntityWithUserDto.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/EntityDto.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/EntityDto.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/FullAuditedEntityDto.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/FullAuditedEntityDto.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/FullAuditedEntityWithUserDto.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/FullAuditedEntityWithUserDto.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/IEntityDto.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/IEntityDto.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/IHasTotalCount.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/IHasTotalCount.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/ILimitedResultRequest.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ILimitedResultRequest.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/IListResult.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/IListResult.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/IPagedAndSortedResultRequest.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/IPagedAndSortedResultRequest.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/IPagedResult.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/IPagedResult.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/IPagedResultRequest.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/IPagedResultRequest.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/ISortedResultRequest.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ISortedResultRequest.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/LimitedResultRequestDto.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/LimitedResultRequestDto.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/ListResultDto.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ListResultDto.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/PagedAndSortedResultRequestDto.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/PagedAndSortedResultRequestDto.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/PagedResultDto.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/PagedResultDto.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/PagedResultRequestDto.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/PagedResultRequestDto.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/IApplicationService.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/IApplicationService.cs

0
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ICrudAppService.cs → framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Services/ICrudAppService.cs

4
framework/src/Volo.Abp.Ddd.Application/Volo.Abp.Ddd.Application.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
@ -15,7 +15,7 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Authorization\Volo.Abp.Authorization.csproj" />
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Ddd.Application.Contracts\Volo.Abp.Ddd.Application.Contracts.csproj" />
<ProjectReference Include="..\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" />
<ProjectReference Include="..\Volo.Abp.Features\Volo.Abp.Features.csproj" />
<ProjectReference Include="..\Volo.Abp.Http.Abstractions\Volo.Abp.Http.Abstractions.csproj" />

2
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs

@ -16,6 +16,7 @@ namespace Volo.Abp.Application
{
[DependsOn(
typeof(AbpDddDomainModule),
typeof(AbpDddApplicationContractsModule),
typeof(AbpSecurityModule),
typeof(AbpObjectMappingModule),
typeof(AbpValidationModule),
@ -30,6 +31,7 @@ namespace Volo.Abp.Application
{
Configure<ApiDescriptionModelOptions>(options =>
{
//TODO: Should we move related items to their own projects?
options.IgnoredInterfaces.AddIfNotContains(typeof(IRemoteService));
options.IgnoredInterfaces.AddIfNotContains(typeof(IApplicationService));
options.IgnoredInterfaces.AddIfNotContains(typeof(IUnitOfWorkEnabled));

10
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -30,9 +30,9 @@ namespace Volo.Abp.EntityFrameworkCore
{
protected virtual Guid? CurrentTenantId => CurrentTenant?.Id;
protected virtual bool IsMultiTenantFilterEnabled => DataFilter.IsEnabled<IMultiTenant>();
protected virtual bool IsMultiTenantFilterEnabled => DataFilter?.IsEnabled<IMultiTenant>() ?? false;
protected virtual bool IsSoftDeleteFilterEnabled => DataFilter.IsEnabled<ISoftDelete>();
protected virtual bool IsSoftDeleteFilterEnabled => DataFilter?.IsEnabled<ISoftDelete>() ?? false;
public ICurrentTenant CurrentTenant { get; set; }
@ -316,17 +316,17 @@ namespace Volo.Abp.EntityFrameworkCore
protected virtual void SetCreationAuditProperties(EntityEntry entry)
{
AuditPropertySetter.SetCreationProperties(entry.Entity);
AuditPropertySetter?.SetCreationProperties(entry.Entity);
}
protected virtual void SetModificationAuditProperties(EntityEntry entry)
{
AuditPropertySetter.SetModificationProperties(entry.Entity);
AuditPropertySetter?.SetModificationProperties(entry.Entity);
}
protected virtual void SetDeletionAuditProperties(EntityEntry entry)
{
AuditPropertySetter.SetDeletionProperties(entry.Entity);
AuditPropertySetter?.SetDeletionProperties(entry.Entity);
}
protected virtual void ConfigureBaseProperties<TEntity>(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType)

4
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs

@ -224,12 +224,16 @@ namespace Volo.Abp.Http.Client.DynamicProxying
requestMessage.Headers.Add(TenantResolverConsts.DefaultTenantKey, CurrentTenant.Id.Value.ToString());
}
//Culture
//TODO: Is that the way we want? Couldn't send the culture (not ui culture)
var currentCulture = CultureInfo.CurrentUICulture.Name ?? CultureInfo.CurrentCulture.Name;
if (!currentCulture.IsNullOrEmpty())
{
requestMessage.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(currentCulture));
}
//X-Requested-With
requestMessage.Headers.Add("X-Requested-With", "XMLHttpRequest");
}
private string GetConfiguredApiVersion()

8
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs

@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.DynamicProxying;
@ -23,11 +22,6 @@ namespace Volo.Abp.Http
{
options.RemoteServices.Default = new RemoteServiceConfiguration("/");
});
//This is needed after ASP.NET Core 3.0 upgrade.
context.Services.AddMvc()
.PartManager.ApplicationParts
.Add(new AssemblyPart(typeof(AbpAspNetCoreMvcModule).Assembly));
}
}
}

11
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs

@ -5,7 +5,7 @@ using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Http.Client;
using Volo.Abp.TestApp.Application;
using Volo.Abp.TestApp.Application.Dto;
using Volo.Abp.TestApp.Domain;
@ -103,6 +103,15 @@ namespace Volo.Abp.Http.DynamicProxying
personInDb.Age.ShouldBe(person.Age);
}
[Fact]
public async Task GetWithAuthorized()
{
await Assert.ThrowsAnyAsync<Exception>(async () =>
{
await _peopleAppService.GetWithAuthorized();
});
}
[Fact]
public async Task GetWithComplexType()
{

2
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs

@ -14,6 +14,8 @@ namespace Volo.Abp.TestApp.Application
Task RemovePhone(Guid id, string number);
Task GetWithAuthorized();
Task<GetWithComplexTypeInput> GetWithComplexType(GetWithComplexTypeInput input);
}
}

7
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.TestApp.Domain;
using Volo.Abp.Domain.Repositories;
@ -46,6 +47,12 @@ namespace Volo.Abp.TestApp.Application
Repository.Update(person);
}
[Authorize]
public Task GetWithAuthorized()
{
return Task.CompletedTask;
}
public Task<GetWithComplexTypeInput> GetWithComplexType(GetWithComplexTypeInput input)
{
return Task.FromResult(input);

2
modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml

@ -87,7 +87,7 @@
</label>
</div>
<select asp-items="Model.ProjectSelectItems" class="form-control" onchange="window.location.replace(this.value)"></select>
<select asp-items="Model.ProjectSelectItems" class="form-control" onchange="window.location.pathname = this.value"></select>
</div>
</div>
</div>

2
modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs

@ -187,7 +187,7 @@ namespace Volo.Docs.Pages.Documents.Project
ProjectSelectItems = projects.Items.Select(p => new SelectListItem
{
Text = p.Name,
Value = p.Id != Project.Id ? "/" + DocumentsUrlPrefix + LanguageCode + "/" + p.ShortName + "/" + DocsAppConsts.Latest : null,
Value = p.Id != Project.Id ? DocumentsUrlPrefix + LanguageCode + "/" + p.ShortName + "/" + DocsAppConsts.Latest : null,
Selected = p.Id == Project.Id
}).ToList();
}

1
npm/ng-packs/.gitignore

@ -3,6 +3,7 @@
# compiled output
/tmp
/out-tsc
/dist/dev-app
# Only exists if Bazel was run
/bazel-out

25
npm/ng-packs/angular.json

@ -434,13 +434,30 @@
"tsConfig": "apps/dev-app/tsconfig.app.json",
"aot": false,
"assets": ["apps/dev-app/src/favicon.ico", "apps/dev-app/src/assets"],
"extractCss": true,
"styles": [
"apps/dev-app/src/styles.scss",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/font-awesome/css/font-awesome.min.css",
"node_modules/primeng/resources/themes/nova-light/theme.css",
"node_modules/primeicons/primeicons.css",
"node_modules/primeng/resources/primeng.min.css"
{
"input": "node_modules/font-awesome/css/font-awesome.min.css",
"lazy": true,
"bundleName": "font-awesome.min"
},
{
"input": "node_modules/primeng/resources/themes/nova-light/theme.css",
"lazy": true,
"bundleName": "primeng-nova-light-theme"
},
{
"input": "node_modules/primeicons/primeicons.css",
"lazy": true,
"bundleName": "primeicons"
},
{
"input": "node_modules/primeng/resources/primeng.min.css",
"lazy": true,
"bundleName": "primeng.min"
}
],
"scripts": []
},

5
npm/ng-packs/apps/dev-app/src/app/app-routing.module.ts

@ -25,6 +25,11 @@ const routes: Routes = [
loadChildren: () =>
import('./lazy-libs/tenant-management-wrapper.module').then(m => m.TenantManagementWrapperModule),
},
{
path: 'setting-management',
loadChildren: () =>
import('./lazy-libs/setting-management-wrapper.module').then(m => m.SettingManagementWrapperModule),
},
];
@NgModule({

18
npm/ng-packs/apps/dev-app/src/app/app.component.ts

@ -1,4 +1,5 @@
import { Component } from '@angular/core';
import { LazyLoadService } from '@abp/ng.core';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-root',
@ -7,4 +8,17 @@ import { Component } from '@angular/core';
<router-outlet></router-outlet>
`,
})
export class AppComponent {}
export class AppComponent implements OnInit {
constructor(private lazyLoadService: LazyLoadService) {}
ngOnInit() {
this.lazyLoadService
.load(
['primeng.min.css', 'primeicons.css', 'primeng-nova-light-theme.css', 'font-awesome.min.css'],
'style',
null,
'head',
)
.subscribe();
}
}

2
npm/ng-packs/apps/dev-app/src/app/app.module.ts

@ -14,6 +14,7 @@ import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { AccountConfigModule } from '@abp/ng.account.config';
import { IdentityConfigModule } from '@abp/ng.identity.config';
import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management.config';
@NgModule({
declarations: [AppComponent],
@ -28,6 +29,7 @@ import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config';
AccountConfigModule.forRoot({ redirectUrl: '/' }),
IdentityConfigModule,
TenantManagementConfigModule,
SettingManagementConfigModule,
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,

7
npm/ng-packs/apps/dev-app/src/app/lazy-libs/setting-management-wrapper.module.ts

@ -0,0 +1,7 @@
import { NgModule } from '@angular/core';
import { SettingManagementModule } from '@abp/ng.setting-management';
@NgModule({
imports: [SettingManagementModule],
})
export class SettingManagementWrapperModule {}

4
npm/ng-packs/packages/account-config/src/lib/services/account-config.service.ts

@ -1,4 +1,4 @@
import { ABP_ROUTES, eLayoutType, RestService } from '@abp/ng.core';
import { eLayoutType, RestService, addAbpRoutes } from '@abp/ng.core';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
@ -7,7 +7,7 @@ import { Router } from '@angular/router';
})
export class AccountConfigService {
constructor(private router: Router, private restService: RestService) {
ABP_ROUTES.push({
addAbpRoutes({
name: 'AbpAccount::Menu:Account',
path: 'account',
invisible: true,

2
npm/ng-packs/packages/account/ng-package.json

@ -4,5 +4,5 @@
"lib": {
"entryFile": "src/public-api.ts"
},
"whitelistedNonPeerDependencies": ["@abp/ng.theme.shared"]
"whitelistedNonPeerDependencies": ["@abp/ng.theme.shared", "abp/ng.account.config"]
}

3
npm/ng-packs/packages/account/package.json

@ -2,7 +2,8 @@
"name": "@abp/ng.account",
"version": "0.9.0",
"dependencies": {
"@abp/ng.theme.shared": "^0.9.0"
"@abp/ng.theme.shared": "^0.9.0",
"abp/ng.account.config": "^0.0.1"
},
"publishConfig": {
"access": "public"

6
npm/ng-packs/packages/account/src/lib/services/account.service.ts

@ -4,7 +4,7 @@ import { RestService, Rest } from '@abp/ng.core';
import { RegisterResponse, RegisterRequest, TenantIdResponse } from '../models';
@Injectable({
providedIn: 'root'
providedIn: 'root',
})
export class AccountService {
constructor(private rest: RestService) {}
@ -12,7 +12,7 @@ export class AccountService {
findTenant(tenantName: string): Observable<TenantIdResponse> {
const request: Rest.Request<null> = {
method: 'GET',
url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`
url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`,
};
return this.rest.request<null, TenantIdResponse>(request);
@ -22,7 +22,7 @@ export class AccountService {
const request: Rest.Request<RegisterRequest> = {
method: 'POST',
url: '/api/account/register',
body
body,
};
return this.rest.request<RegisterRequest, RegisterResponse>(request, { skipHandleError: true });

3
npm/ng-packs/packages/core/src/lib/core.module.ts

@ -27,6 +27,7 @@ import { getInitialData, localeInitializer } from './utils/initial-utils';
import { ConfigPlugin, NGXS_CONFIG_PLUGIN_OPTIONS } from './plugins/config/config.plugin';
import { ForDirective } from './directives/for.directive';
import { AbstractNgModelComponent } from './abstracts/ng-model.component';
import { SortDirective } from './directives/sort.directive';
@NgModule({
imports: [
@ -46,6 +47,7 @@ import { AbstractNgModelComponent } from './abstracts/ng-model.component';
EllipsisDirective,
ForDirective,
FormSubmitDirective,
SortDirective,
LocalizationPipe,
SortPipe,
PermissionDirective,
@ -68,6 +70,7 @@ import { AbstractNgModelComponent } from './abstracts/ng-model.component';
FormSubmitDirective,
LocalizationPipe,
SortPipe,
SortDirective,
PermissionDirective,
VisibilityDirective,
InputEventDebounceDirective,

12
npm/ng-packs/packages/core/src/lib/directives/sort.directive.ts

@ -0,0 +1,12 @@
import { Directive, ElementRef, Input, Optional, Self } from '@angular/core';
import { Table } from 'primeng/table';
@Directive({
selector: '[abpSort]',
})
export class SortDirective {
constructor(private elementRef: ElementRef, @Optional() @Self() table: Table) {
console.warn(elementRef);
setInterval(() => console.warn(table.value), 1000);
}
}

14
npm/ng-packs/packages/core/src/lib/models/application-configuration.ts

@ -1,10 +1,12 @@
import { ABP } from './common';
export namespace ApplicationConfiguration {
export interface Response {
localization: Localization;
auth: Auth;
setting: Setting;
setting: Value;
currentUser: CurrentUser;
features: Features;
features: Value;
}
export interface Localization {
@ -32,8 +34,8 @@ export namespace ApplicationConfiguration {
[key: string]: boolean;
}
export interface Setting {
values: { [key: string]: 'Abp.Localization.DefaultLanguage' };
export interface Value {
values: ABP.Dictionary<string>;
}
export interface CurrentUser {
@ -42,8 +44,4 @@ export namespace ApplicationConfiguration {
tenantId: string;
userName: string;
}
export interface Features {
values: Setting;
}
}

26
npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts

@ -1,14 +1,28 @@
import { Pipe, PipeTransform } from '@angular/core';
import clone from 'just-clone';
export type SortOrder = 'asc' | 'desc';
@Pipe({
name: 'abpSort',
// tslint:disable-next-line: no-pipe-impure
pure: false
})
export class SortPipe implements PipeTransform {
transform(value: any[], sortOrder: string): any {
sortOrder = sortOrder.toLowerCase();
if (sortOrder === 'desc') return value.reverse();
else return value;
intialValue: any[];
transform(value: any[], sortOrder: SortOrder | string = 'asc', sortKey?: string): any {
sortOrder = sortOrder && (sortOrder.toLowerCase() as any);
if (!this.intialValue) this.intialValue = clone(value);
if (!value || (sortOrder !== 'asc' && sortOrder !== 'desc')) return this.intialValue;
let sorted;
if (!sortKey) {
sorted = value.sort();
} else {
sorted = value.sort((a, b) => (a[sortKey] < b[sortKey] ? -1 : a[sortKey] > b[sortKey] ? 1 : 0));
}
return sortOrder === 'asc' ? sorted : sorted.reverse();
}
}

17
npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts

@ -3,13 +3,11 @@ import { Router, Routes } from '@angular/router';
import { actionMatcher, InitState, NgxsNextPluginFn, NgxsPlugin, setValue, UpdateState } from '@ngxs/store';
import snq from 'snq';
import { ABP } from '../../models';
import { organizeRoutes } from '../../utils/route-utils';
import { organizeRoutes, getAbpRoutes } from '../../utils/route-utils';
import clone from 'just-clone';
export const NGXS_CONFIG_PLUGIN_OPTIONS = new InjectionToken('NGXS_CONFIG_PLUGIN_OPTIONS');
export let ABP_ROUTES = [] as ABP.FullRoute[];
@Injectable()
export class ConfigPlugin implements NgxsPlugin {
private initialized = false;
@ -42,24 +40,21 @@ export class ConfigPlugin implements NgxsPlugin {
}
function transformRoutes(routes: Routes = [], wrappers: ABP.FullRoute[] = []): any {
/**
*
* @deprecated since version 0.9.0
*/
const abpRoutes: ABP.FullRoute[] = routes
// TODO: remove in v1
const oldAbpRoutes: ABP.FullRoute[] = routes
.filter(route => {
return snq(() => route.data.routes.routes.find(r => r.path === route.path), false);
})
.reduce((acc, val) => [...acc, ...val.data.routes.routes], []);
// tslint:disable-next-line: deprecation
ABP_ROUTES = [...ABP_ROUTES, ...abpRoutes];
const abpRoutes = [...getAbpRoutes(), ...oldAbpRoutes];
wrappers = ABP_ROUTES.filter(ar => ar.wrapper);
wrappers = abpRoutes.filter(ar => ar.wrapper);
const transformed = [] as ABP.FullRoute[];
routes
.filter(route => route.component || route.loadChildren)
.forEach(route => {
const abpPackage = ABP_ROUTES.find(abp => abp.path.toLowerCase() === route.path.toLowerCase() && !abp.wrapper);
const abpPackage = abpRoutes.find(abp => abp.path.toLowerCase() === route.path.toLowerCase() && !abp.wrapper);
const { length } = transformed;

86
npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts

@ -9,50 +9,68 @@ export class LazyLoadService {
loadedLibraries: { [url: string]: ReplaySubject<void> } = {};
load(
url: string,
urlOrUrls: string | string[],
type: 'script' | 'style',
content: string = '',
targetQuery: string = 'body',
position: InsertPosition = 'afterend',
): Observable<void> {
if (!url && !content) return;
const key = url ? url.slice(url.lastIndexOf('/') + 1) : uuid();
if (this.loadedLibraries[key]) {
return this.loadedLibraries[key].asObservable();
if (!urlOrUrls && !content) {
return;
} else if (!urlOrUrls && content) {
urlOrUrls = [null];
}
this.loadedLibraries[key] = new ReplaySubject();
let library;
if (type === 'script') {
library = document.createElement('script');
library.type = 'text/javascript';
if (url) {
(library as HTMLScriptElement).src = url;
}
(library as HTMLScriptElement).text = content;
} else if (url) {
library = document.createElement('link');
library.type = 'text/css';
(library as HTMLLinkElement).rel = 'stylesheet';
if (url) {
(library as HTMLLinkElement).href = url;
}
} else {
library = document.createElement('style');
(library as HTMLStyleElement).textContent = content;
if (!Array.isArray(urlOrUrls)) {
urlOrUrls = [urlOrUrls];
}
library.onload = () => {
this.loadedLibraries[key].next();
this.loadedLibraries[key].complete();
};
return new Observable(subscriber => {
(urlOrUrls as string[]).forEach((url, index) => {
const key = url ? url.slice(url.lastIndexOf('/') + 1) : uuid();
if (this.loadedLibraries[key]) {
subscriber.next();
subscriber.complete();
return;
}
this.loadedLibraries[key] = new ReplaySubject();
let library;
if (type === 'script') {
library = document.createElement('script');
library.type = 'text/javascript';
if (url) {
(library as HTMLScriptElement).src = url;
}
(library as HTMLScriptElement).text = content;
} else if (url) {
library = document.createElement('link');
library.type = 'text/css';
(library as HTMLLinkElement).rel = 'stylesheet';
if (url) {
(library as HTMLLinkElement).href = url;
}
} else {
library = document.createElement('style');
(library as HTMLStyleElement).textContent = content;
}
library.onload = () => {
this.loadedLibraries[key].next();
this.loadedLibraries[key].complete();
document.querySelector(targetQuery).insertAdjacentElement(position, library);
if (index === urlOrUrls.length - 1) {
subscriber.next();
subscriber.complete();
}
};
return this.loadedLibraries[key].asObservable();
document.querySelector(targetQuery).insertAdjacentElement(position, library);
});
});
}
}

36
npm/ng-packs/packages/core/src/lib/states/config.state.ts

@ -1,14 +1,14 @@
import { State, Selector, createSelector, Action, StateContext, Store } from '@ngxs/store';
import { Config } from '../models/config';
import { ABP } from '../models/common';
import { GetAppConfiguration, PatchRouteByName } from '../actions/config.actions';
import { ApplicationConfigurationService } from '../services/application-configuration.service';
import { tap, switchMap } from 'rxjs/operators';
import { Action, createSelector, Selector, State, StateContext, Store } from '@ngxs/store';
import { of } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import snq from 'snq';
import { GetAppConfiguration, PatchRouteByName } from '../actions/config.actions';
import { SetLanguage } from '../actions/session.actions';
import { ABP } from '../models/common';
import { Config } from '../models/config';
import { ApplicationConfigurationService } from '../services/application-configuration.service';
import { organizeRoutes } from '../utils/route-utils';
import { SessionState } from './session.state';
import { of } from 'rxjs';
import { setChildRoute, sortRoutes, organizeRoutes } from '../utils/route-utils';
@State<Config.State>({
name: 'ConfigState',
@ -90,14 +90,31 @@ export class ConfigState {
return selector;
}
static getSetting(key: string) {
static getSetting(key: string, findContain?: boolean) {
const selector = createSelector(
[ConfigState],
(state: Config.State) => {
return snq(() => state.setting.values[key]);
},
);
return selector;
}
static getSettings(keyword?: string) {
const selector = createSelector(
[ConfigState],
(state: Config.State) => {
if (keyword) {
const keys = snq(() => Object.keys(state.setting.values).filter(key => key.indexOf(keyword) > -1), []);
if (keys.length) {
return keys.reduce((acc, key) => ({ ...acc, [key]: state.setting.values[key] }), {});
}
}
return snq(() => state.setting.values, {});
},
);
return selector;
}
@ -213,6 +230,7 @@ export class ConfigState {
});
}
if (typeof localization !== 'string') localization = '';
return localization || defaultValue || key;
},
);

6
npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts

@ -4,13 +4,13 @@ import { NGXS_PLUGINS, NgxsModule, InitState, Store } from '@ngxs/store';
import { environment } from '../../../../../apps/dev-app/src/environments/environment';
import { LAYOUTS } from '../../../../theme-basic/src/public-api';
import { ABP } from '../models';
import { ConfigPlugin, NGXS_CONFIG_PLUGIN_OPTIONS, ABP_ROUTES } from '../plugins';
import { ConfigPlugin, NGXS_CONFIG_PLUGIN_OPTIONS, addAbpRoutes } from '../plugins';
import { RouterOutletComponent } from '../components';
import { ConfigState } from '../states';
import { CoreModule } from '../core.module';
import { eLayoutType } from '../enums/common';
ABP_ROUTES.push(
addAbpRoutes([
{
name: 'AbpUiNavigation::Menu:Administration',
path: '',
@ -54,7 +54,7 @@ ABP_ROUTES.push(
},
],
},
);
]);
const expectedState = {
environment,

14
npm/ng-packs/packages/core/src/lib/utils/route-utils.ts

@ -60,3 +60,17 @@ export function sortRoutes(routes: ABP.FullRoute[] = []): ABP.FullRoute[] {
return route;
});
}
const ABP_ROUTES = [] as ABP.FullRoute[];
export function addAbpRoutes(routes: ABP.FullRoute | ABP.FullRoute[]): void {
if (!Array.isArray(routes)) {
routes = [routes];
}
ABP_ROUTES.push(...routes);
}
export function getAbpRoutes(): ABP.FullRoute[] {
return ABP_ROUTES;
}

1
npm/ng-packs/packages/core/src/public-api.ts

@ -12,6 +12,7 @@ export * from './lib/enums';
export * from './lib/guards';
export * from './lib/interceptors';
export * from './lib/models';
export * from './lib/pipes';
export * from './lib/plugins';
export * from './lib/services';
export * from './lib/states';

6
npm/ng-packs/packages/identity-config/src/lib/services/identity-config.service.ts

@ -1,4 +1,4 @@
import { ABP_ROUTES, eLayoutType, RestService } from '@abp/ng.core';
import { addAbpRoutes, eLayoutType, RestService } from '@abp/ng.core';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
@ -8,7 +8,7 @@ import { Observable } from 'rxjs';
})
export class IdentityConfigService {
constructor(private router: Router, private restService: RestService) {
ABP_ROUTES.push(
addAbpRoutes([
{
name: 'AbpUiNavigation::Menu:Administration',
path: '',
@ -27,6 +27,6 @@ export class IdentityConfigService {
{ path: 'users', name: 'AbpIdentity::Users', order: 1, requiredPolicy: 'AbpIdentity.Users' },
],
},
);
]);
}
}

13
npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html

@ -24,7 +24,7 @@
</div>
<p-table
*ngIf="[130, 200] as columnWidths"
[value]="data$ | async"
[value]="data$ | async | abpSort: sortOrder:sortKey"
[lazy]="true"
[lazyLoadOnInit]="false"
[paginator]="true"
@ -51,11 +51,14 @@
<ng-template pTemplate="header" let-columns>
<tr>
<th>{{ 'AbpIdentity::Actions' | abpLocalization }}</th>
<th pResizableColumn (click)="changeSortOrder()">
<th pResizableColumn (click)="sortOrderIcon.sort('name')">
{{ 'AbpIdentity::RoleName' | abpLocalization }}
<span class="float-right"
><i [ngClass]="['fa', sortOrder === 'desc' ? 'fa-sort-desc' : 'fa-sort-asc']"></i
></span>
<abp-sort-order-icon
#sortOrderIcon
key="name"
[(selectedKey)]="sortKey"
[(order)]="sortOrder"
></abp-sort-order-icon>
</th>
</tr>
</ng-template>

22
npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts

@ -11,7 +11,7 @@ import { IdentityState } from '../../states/identity.state';
@Component({
selector: 'abp-roles',
templateUrl: './roles.component.html'
templateUrl: './roles.component.html',
})
export class RolesComponent {
@Select(IdentityState.getRoles)
@ -30,15 +30,15 @@ export class RolesComponent {
providerKey: string;
pageQuery: ABP.PageQueryParams = {
sorting: 'name'
};
pageQuery: ABP.PageQueryParams = {};
loading = false;
modalBusy = false;
sortOrder = 'asc';
sortOrder = '';
sortKey = '';
@ViewChild('modalContent', { static: false })
modalContent: TemplateRef<any>;
@ -54,10 +54,10 @@ export class RolesComponent {
this.form = this.fb.group({
name: new FormControl({ value: this.selected.name || '', disabled: this.selected.isStatic }, [
Validators.required,
Validators.maxLength(256)
Validators.maxLength(256),
]),
isDefault: [this.selected.isDefault || false],
isPublic: [this.selected.isPublic || false]
isPublic: [this.selected.isPublic || false],
});
}
@ -89,7 +89,7 @@ export class RolesComponent {
.dispatch(
this.selected.id
? new UpdateRole({ ...this.form.value, id: this.selected.id })
: new CreateRole(this.form.value)
: new CreateRole(this.form.value),
)
.subscribe(() => {
this.modalBusy = false;
@ -100,7 +100,7 @@ export class RolesComponent {
delete(id: string, name: string) {
this.confirmationService
.warn('AbpIdentity::RoleDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', {
messageLocalizationParams: [name]
messageLocalizationParams: [name],
})
.subscribe((status: Toaster.Status) => {
if (status === Toaster.Status.confirm) {
@ -123,8 +123,4 @@ export class RolesComponent {
.pipe(finalize(() => (this.loading = false)))
.subscribe();
}
changeSortOrder() {
this.sortOrder = this.sortOrder.toLowerCase() === 'asc' ? 'desc' : 'asc';
}
}

28
npm/ng-packs/packages/identity/src/lib/components/users/users.component.html

@ -29,8 +29,9 @@
/></label>
</div>
<p-table
abpSort
*ngIf="[130, 200, 200, 200] as columnWidths"
[value]="data$ | async"
[value]="data$ | async | abpSort: sortOrder:sortKey"
[lazy]="true"
[lazyLoadOnInit]="false"
[paginator]="true"
@ -57,14 +58,27 @@
<ng-template pTemplate="header">
<tr>
<th>{{ 'AbpIdentity::Actions' | abpLocalization }}</th>
<th pResizableColumn (click)="changeSortOrder()">
<th pResizableColumn (click)="sortOrderIcon.sort('userName')">
{{ 'AbpIdentity::UserName' | abpLocalization }}
<span class="float-right"
><i [ngClass]="['fa', sortOrder === 'desc' ? 'fa-sort-desc' : 'fa-sort-asc']"></i
></span>
<abp-sort-order-icon
#sortOrderIcon
key="userName"
[(selectedKey)]="sortKey"
[(order)]="sortOrder"
></abp-sort-order-icon>
</th>
<th pResizableColumn (click)="sortOrderIcon.sort('email')">
{{ 'AbpIdentity::EmailAddress' | abpLocalization }}
<abp-sort-order-icon key="email" [(selectedKey)]="sortKey" [(order)]="sortOrder"></abp-sort-order-icon>
</th>
<th pResizableColumn (click)="sortOrderIcon.sort('phoneNumber')">
{{ 'AbpIdentity::PhoneNumber' | abpLocalization }}
<abp-sort-order-icon
key="phoneNumber"
[(selectedKey)]="sortKey"
[(order)]="sortOrder"
></abp-sort-order-icon>
</th>
<th pResizableColumn>{{ 'AbpIdentity::EmailAddress' | abpLocalization }}</th>
<th pResizableColumn>{{ 'AbpIdentity::PhoneNumber' | abpLocalization }}</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-data>

36
npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts

@ -12,13 +12,13 @@ import {
GetUserById,
GetUserRoles,
GetUsers,
UpdateUser
UpdateUser,
} from '../../actions/identity.actions';
import { Identity } from '../../models/identity';
import { IdentityState } from '../../states/identity.state';
@Component({
selector: 'abp-users',
templateUrl: './users.component.html'
templateUrl: './users.component.html',
})
export class UsersComponent {
@Select(IdentityState.getUsers)
@ -42,9 +42,7 @@ export class UsersComponent {
providerKey: string;
pageQuery: ABP.PageQueryParams = {
sorting: 'userName'
};
pageQuery: ABP.PageQueryParams = {};
isModalVisible: boolean;
@ -52,7 +50,9 @@ export class UsersComponent {
modalBusy = false;
sortOrder = 'asc';
sortOrder = '';
sortKey = '';
trackByFn: TrackByFunction<AbstractControl> = (index, item) => Object.keys(item)[0] || index;
@ -80,10 +80,10 @@ export class UsersComponent {
roleNames: this.fb.array(
this.roles.map(role =>
this.fb.group({
[role.name]: [!!snq(() => this.selectedUserRoles.find(userRole => userRole.id === role.id))]
})
)
)
[role.name]: [!!snq(() => this.selectedUserRoles.find(userRole => userRole.id === role.id))],
}),
),
),
});
if (!this.selected.userName) {
this.form.addControl('password', new FormControl('', [Validators.required, Validators.maxLength(32)]));
@ -107,7 +107,7 @@ export class UsersComponent {
.pipe(
switchMap(() => this.store.dispatch(new GetUserRoles(id))),
pluck('IdentityState'),
take(1)
take(1),
)
.subscribe((state: Identity.State) => {
this.selected = state.selectedUser;
@ -123,7 +123,7 @@ export class UsersComponent {
const { roleNames } = this.form.value;
const mappedRoleNames = snq(
() => roleNames.filter(role => !!role[Object.keys(role)[0]]).map(role => Object.keys(role)[0]),
[]
[],
);
this.store
@ -132,12 +132,12 @@ export class UsersComponent {
? new UpdateUser({
...this.form.value,
id: this.selected.id,
roleNames: mappedRoleNames
roleNames: mappedRoleNames,
})
: new CreateUser({
...this.form.value,
roleNames: mappedRoleNames
})
roleNames: mappedRoleNames,
}),
)
.subscribe(() => {
this.modalBusy = false;
@ -148,7 +148,7 @@ export class UsersComponent {
delete(id: string, userName: string) {
this.confirmationService
.warn('AbpIdentity::UserDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', {
messageLocalizationParams: [userName]
messageLocalizationParams: [userName],
})
.subscribe((status: Toaster.Status) => {
if (status === Toaster.Status.confirm) {
@ -171,8 +171,4 @@ export class UsersComponent {
.pipe(finalize(() => (this.loading = false)))
.subscribe();
}
changeSortOrder() {
this.sortOrder = this.sortOrder.toLowerCase() === 'asc' ? 'desc' : 'asc';
}
}

19
npm/ng-packs/packages/setting-management-config/src/lib/services/setting-management-config.service.ts

@ -1,18 +1,29 @@
import { Injectable } from '@angular/core';
import { ABP_ROUTES, eLayoutType } from '@abp/ng.core';
import { addAbpRoutes, eLayoutType, PatchRouteByName, ABP } from '@abp/ng.core';
import { getSettingTabs } from '@abp/ng.theme.shared';
import { Store } from '@ngxs/store';
@Injectable({
providedIn: 'root',
})
export class SettingManagementConfigService {
constructor() {
ABP_ROUTES.push({
name: 'Settings',
constructor(private store: Store) {
const route = {
name: 'AbpSettingManagement::Settings',
path: 'setting-management',
parentName: 'AbpUiNavigation::Menu:Administration',
layout: eLayoutType.application,
order: 6,
iconClass: 'fa fa-cog',
} as ABP.FullRoute;
addAbpRoutes(route);
setTimeout(() => {
const tabs = getSettingTabs();
if (!tabs || !tabs.length) {
this.store.dispatch(new PatchRouteByName('AbpSettingManagement::Settings', { ...route, invisible: true }));
}
});
}
}

8
npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.ts

@ -1,5 +1,5 @@
import { Component, TrackByFunction, OnInit } from '@angular/core';
import { SettingTab, SETTING_TABS } from '@abp/ng.theme.shared';
import { SettingTab, getSettingTabs } from '@abp/ng.theme.shared';
import { Router } from '@angular/router';
import { Store } from '@ngxs/store';
import { ConfigState } from '@abp/ng.core';
@ -18,9 +18,9 @@ export class SettingManagementComponent implements OnInit {
constructor(private router: Router, private store: Store) {}
ngOnInit() {
this.settings = SETTING_TABS.filter(setting =>
this.store.selectSnapshot(ConfigState.getGrantedPolicy(setting.requiredPolicy)),
).sort((a, b) => a.order - b.order);
this.settings = getSettingTabs()
.filter(setting => this.store.selectSnapshot(ConfigState.getGrantedPolicy(setting.requiredPolicy)))
.sort((a, b) => a.order - b.order);
if (this.settings.length) {
this.selected = this.settings[0];
}

4
npm/ng-packs/packages/tenant-management-config/src/lib/services/tenant-management-config.service.ts

@ -1,12 +1,12 @@
import { Injectable } from '@angular/core';
import { ABP_ROUTES, eLayoutType } from '@abp/ng.core';
import { addAbpRoutes, eLayoutType } from '@abp/ng.core';
@Injectable({
providedIn: 'root',
})
export class TenantManagementConfigService {
constructor() {
ABP_ROUTES.push({
addAbpRoutes({
name: 'AbpTenantManagement::Menu:TenantManagement',
path: 'tenant-management',
parentName: 'AbpUiNavigation::Menu:Administration',

13
npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html

@ -31,7 +31,7 @@
</div>
<p-table
*ngIf="[130, 200] as columnWidths"
[value]="data$ | async | abpSort: sortOrder"
[value]="data$ | async | abpSort: sortOrder:sortKey"
[lazy]="true"
[lazyLoadOnInit]="false"
[paginator]="true"
@ -58,11 +58,14 @@
<ng-template pTemplate="header" let-columns>
<tr>
<th>{{ 'AbpTenantManagement::Actions' | abpLocalization }}</th>
<th pResizableColumn (click)="changeSortOrder()">
<th pResizableColumn (click)="sortOrderIcon.sort('name')">
{{ 'AbpTenantManagement::TenantName' | abpLocalization }}
<span class="float-right"
><i [ngClass]="['fa', sortOrder === 'desc' ? 'fa-sort-desc' : 'fa-sort-asc']"></i
></span>
<abp-sort-order-icon
#sortOrderIcon
key="name"
[(selectedKey)]="sortKey"
[(order)]="sortOrder"
></abp-sort-order-icon>
</th>
</tr>
</ng-template>

34
npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts

@ -10,7 +10,7 @@ import {
DeleteTenant,
GetTenants,
GetTenantById,
UpdateTenant
UpdateTenant,
} from '../../actions/tenant-management.actions';
import { TenantManagementService } from '../../services/tenant-management.service';
import { TenantManagementState } from '../../states/tenant-management.state';
@ -23,7 +23,7 @@ interface SelectedModalContent {
@Component({
selector: 'abp-tenants',
templateUrl: './tenants.component.html'
templateUrl: './tenants.component.html',
})
export class TenantsComponent {
@Select(TenantManagementState.get)
@ -50,15 +50,15 @@ export class TenantsComponent {
_useSharedDatabase: boolean;
pageQuery: ABP.PageQueryParams = {
sorting: 'name'
};
pageQuery: ABP.PageQueryParams = {};
loading = false;
modalBusy = false;
sortOrder = 'asc';
sortOrder = '';
sortKey = '';
get useSharedDatabase(): boolean {
return this.defaultConnectionStringForm.get('useSharedDatabase').value;
@ -78,7 +78,7 @@ export class TenantsComponent {
private confirmationService: ConfirmationService,
private tenantService: TenantManagementService,
private fb: FormBuilder,
private store: Store
private store: Store,
) {}
onSearch(value) {
@ -88,14 +88,14 @@ export class TenantsComponent {
private createTenantForm() {
this.tenantForm = this.fb.group({
name: [this.selected.name || '', [Validators.required, Validators.maxLength(256)]]
name: [this.selected.name || '', [Validators.required, Validators.maxLength(256)]],
});
}
private createDefaultConnectionStringForm() {
this.defaultConnectionStringForm = this.fb.group({
useSharedDatabase: this._useSharedDatabase,
defaultConnectionString: [this.defaultConnectionString || '']
defaultConnectionString: [this.defaultConnectionString || ''],
});
}
@ -103,7 +103,7 @@ export class TenantsComponent {
this.selectedModalContent = {
title,
template,
type
type,
};
this.isModalVisible = true;
@ -117,7 +117,7 @@ export class TenantsComponent {
switchMap(selected => {
this.selected = selected;
return this.tenantService.getDefaultConnectionString(id);
})
}),
)
.subscribe(fetchedConnectionString => {
this._useSharedDatabase = fetchedConnectionString ? false : true;
@ -158,7 +158,7 @@ export class TenantsComponent {
.deleteDefaultConnectionString(this.selected.id)
.pipe(
take(1),
finalize(() => (this.modalBusy = false))
finalize(() => (this.modalBusy = false)),
)
.subscribe(() => {
this.isModalVisible = false;
@ -168,7 +168,7 @@ export class TenantsComponent {
.updateDefaultConnectionString({ id: this.selected.id, defaultConnectionString: this.connectionString })
.pipe(
take(1),
finalize(() => (this.modalBusy = false))
finalize(() => (this.modalBusy = false)),
)
.subscribe(() => {
this.isModalVisible = false;
@ -184,7 +184,7 @@ export class TenantsComponent {
.dispatch(
this.selected.id
? new UpdateTenant({ ...this.tenantForm.value, id: this.selected.id })
: new CreateTenant(this.tenantForm.value)
: new CreateTenant(this.tenantForm.value),
)
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
@ -195,7 +195,7 @@ export class TenantsComponent {
delete(id: string, name: string) {
this.confirmationService
.warn('AbpTenantManagement::TenantDeletionConfirmationMessage', 'AbpTenantManagement::AreYouSure', {
messageLocalizationParams: [name]
messageLocalizationParams: [name],
})
.subscribe((status: Toaster.Status) => {
if (status === Toaster.Status.confirm) {
@ -218,8 +218,4 @@ export class TenantsComponent {
.pipe(finalize(() => (this.loading = false)))
.subscribe();
}
changeSortOrder() {
this.sortOrder = this.sortOrder.toLowerCase() === 'asc' ? 'desc' : 'asc';
}
}

2
npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.html

@ -11,7 +11,7 @@
</nav>
<div
[@routeAnimations]="outlet && outlet.activatedRoute && outlet.activatedRoute.routeConfig.path"
[@slideFromBottom]="outlet && outlet.activatedRoute && outlet.activatedRoute.routeConfig.path"
style="padding-top: 5rem;"
class="container"
>

9
npm/ng-packs/packages/theme-shared/src/lib/animations/slide.animations.ts

@ -1,6 +1,7 @@
import { animate, state, style, transition, trigger, query } from '@angular/animations';
export const slideFromBottom = trigger('routeAnimations', [
state('void', style({ 'margin-top': '20px', opacity: '0' })),
state('*', style({ 'margin-top': '0px', opacity: '1' })),
transition(':enter', [animate('0.2s ease-out', style({ opacity: '1', 'margin-top': '0px' }))]),
export const slideFromBottom = trigger('slideFromBottom', [
transition('* <=> *', [
style({ 'margin-top': '20px', opacity: '0' }),
animate('0.2s ease-out', style({ opacity: '1', 'margin-top': '0px' })),
]),
]);

9
npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts

@ -6,14 +6,14 @@ import { Component, Input } from '@angular/core';
<button [attr.type]="type" [ngClass]="buttonClass" [disabled]="loading || disabled">
<i [ngClass]="icon" class="mr-1"></i><ng-content></ng-content>
</button>
`
`,
})
export class ButtonComponent {
@Input()
buttonClass = 'btn btn-primary';
@Input()
type = 'button';
buttonType; // TODO: Add initial value.
@Input()
iconClass: string;
@ -24,6 +24,11 @@ export class ButtonComponent {
@Input()
disabled = false;
/**
* @deprecated Use buttonType instead. To be deleted in v1
*/
@Input() type = 'button';
get icon(): string {
return `${this.loading ? 'fa fa-pulse fa-spinner' : this.iconClass || 'd-none'}`;
}

1
npm/ng-packs/packages/theme-shared/src/lib/components/index.ts

@ -8,3 +8,4 @@ export * from './modal/modal.component';
export * from './profile/profile.component';
export * from './table-empty-message/table-empty-message.component';
export * from './toast/toast.component';
export * from './sort-order-icon/sort-order-icon.component';

3
npm/ng-packs/packages/theme-shared/src/lib/components/sort-order-icon/sort-order-icon.component.html

@ -0,0 +1,3 @@
<span class="float-right {{ iconClass }}">
<i class="fa {{ icon }}"></i>
</span>

61
npm/ng-packs/packages/theme-shared/src/lib/components/sort-order-icon/sort-order-icon.component.ts

@ -0,0 +1,61 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'abp-sort-order-icon',
templateUrl: './sort-order-icon.component.html',
})
export class SortOrderIconComponent {
private _order: string;
private _selectedKey: string;
@Input()
set selectedKey(value: string) {
this._selectedKey = value;
this.selectedKeyChange.emit(value);
}
get selectedKey(): string {
return this._selectedKey;
}
@Output() readonly selectedKeyChange = new EventEmitter<string>();
@Input()
key: string;
@Input()
set order(value: string) {
this._order = value;
this.orderChange.emit(value);
}
get order(): string {
return this._order;
}
@Output() readonly orderChange = new EventEmitter<string>();
@Input()
iconClass: string;
get icon(): string {
if (!this.selectedKey) return 'fa-sort';
if (this.selectedKey === this.key) return `fa-sort-${this.order}`;
else return '';
}
sort(key: string) {
this.selectedKey = key;
switch (this.order) {
case '':
this.order = 'asc';
break;
case 'asc':
this.order = 'desc';
this.orderChange.emit('desc');
break;
case 'desc':
this.order = '';
this.selectedKey = '';
break;
}
}
}

2
npm/ng-packs/packages/theme-shared/src/lib/contants/styles.ts

@ -38,7 +38,7 @@ export default `
left: 0 !important;
width: 100% !important;
height: 100% !important;
background-color: rgba(0, 0, 0, .6) !important;
background-color: rgba(0, 0, 0, 0.6) !important;
z-index: 1040 !important;
}

14
npm/ng-packs/packages/theme-shared/src/lib/models/setting-management.ts

@ -7,4 +7,16 @@ export interface SettingTab {
requiredPolicy?: string;
}
export const SETTING_TABS = [] as SettingTab[];
const SETTING_TABS = [] as SettingTab[];
export function addSettingTab(tab: SettingTab | SettingTab[]): void {
if (!Array.isArray(tab)) {
tab = [tab];
}
SETTING_TABS.push(...tab);
}
export function getSettingTabs(): SettingTab[] {
return SETTING_TABS;
}

21
npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts

@ -14,6 +14,7 @@ import { LoaderBarComponent } from './components/loader-bar/loader-bar.component
import { ModalComponent } from './components/modal/modal.component';
import { ProfileComponent } from './components/profile/profile.component';
import { ToastComponent } from './components/toast/toast.component';
import { SortOrderIconComponent } from './components/sort-order-icon/sort-order-icon.component';
import styles from './contants/styles';
import { ErrorHandler } from './handlers/error.handler';
import { chartJsLoaded$ } from './utils/widget-utils';
@ -32,9 +33,9 @@ export function appendScript(injector: Injector) {
'style',
styles,
'head',
'afterbegin'
) /* lazyLoadService.load(null, 'script', scripts) */
).pipe(take(1));
'afterbegin',
) /* lazyLoadService.load(null, 'script', scripts) */,
).toPromise();
};
return fn;
@ -53,7 +54,8 @@ export function appendScript(injector: Injector) {
ModalComponent,
ProfileComponent,
TableEmptyMessageComponent,
ToastComponent
ToastComponent,
SortOrderIconComponent,
],
exports: [
BreadcrumbComponent,
@ -65,9 +67,10 @@ export function appendScript(injector: Injector) {
ModalComponent,
ProfileComponent,
TableEmptyMessageComponent,
ToastComponent
ToastComponent,
SortOrderIconComponent,
],
entryComponents: [ErrorComponent]
entryComponents: [ErrorComponent],
})
export class ThemeSharedModule {
static forRoot(): ModuleWithProviders {
@ -78,10 +81,10 @@ export class ThemeSharedModule {
provide: APP_INITIALIZER,
multi: true,
deps: [Injector, ErrorHandler],
useFactory: appendScript
useFactory: appendScript,
},
{ provide: MessageService, useClass: MessageService }
]
{ provide: MessageService, useClass: MessageService },
],
};
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save