Browse Source

Merge branch 'rel-4.0' of https://github.com/abpframework/abp into refactor-abp-bundle

pull/6281/head
Ilkay Ilknur 5 years ago
parent
commit
d3bbfc7a57
  1. 18
      docs/en/CLI.md
  2. 11
      docs/en/Tutorials/Part-10.md
  3. 12
      docs/en/Tutorials/Part-5.md
  4. 2
      docs/en/UI/Angular/Permission-Management.md
  5. 2
      docs/en/UI/Blazor/Customization-Overriding-Components.md
  6. 37
      docs/en/UI/Blazor/Global-Scripts-Styles.md
  7. 37
      docs/en/UI/Blazor/Message.md
  8. 33
      docs/en/UI/Blazor/Overall.md
  9. 61
      docs/en/UI/Blazor/Page-Alerts.md
  10. 2
      docs/en/UI/Blazor/Theming.md
  11. BIN
      docs/en/images/blazor-page-alert-example.png
  12. BIN
      docs/en/images/lepton-theme-blazor-layout.png
  13. 3
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Alerts/AlertManager.cs
  14. 7
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Alerts/IAlertManager.cs
  15. 6
      framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs
  16. 11
      framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertList.cs
  17. 4
      framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertMessage.cs
  18. 10
      framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertType.cs
  19. 13
      framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/IAlertManager.cs
  20. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs
  21. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs
  22. 15
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js
  23. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs
  24. 2
      framework/src/Volo.Abp.BlazoriseUI/Components/AlertWrapper.cs
  25. 4
      framework/src/Volo.Abp.BlazoriseUI/Components/PageAlert.razor.cs
  26. 14
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextMySQLExtensions.cs
  27. 1
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextOracleDevartExtensions.cs
  28. 1
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextOracleExtensions.cs
  29. 12
      framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextPostgreSqlExtensions.cs
  30. 12
      framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextSqlServerExtensions.cs
  31. 12
      framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextSqliteExtensions.cs
  32. 3
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs
  33. 18
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/EntityExtensionConfiguration.cs
  34. 1
      modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/AbpAuditLoggingEfCoreQueryableExtensions.cs
  35. 6
      modules/docs/app/VoloDocs.Web/Utils/GlobalExceptionHandlerMiddleware.cs
  36. 1
      modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityEfCoreQueryableExtensions.cs
  37. 37
      modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml
  38. 37
      modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml
  39. 37
      modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml
  40. 37
      modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml
  41. 2
      modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/AbpIdentityServerEfCoreQueryableExtensions.cs
  42. 37
      modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml
  43. 37
      modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml
  44. 2
      npm/ng-packs/angular.json
  45. 47
      npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts
  46. 10
      npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts
  47. 2
      npm/ng-packs/packages/core/src/lib/services/config-state.service.ts
  48. 2
      npm/ng-packs/packages/core/src/lib/services/environment.service.ts
  49. 18
      npm/ng-packs/packages/core/src/lib/services/permission.service.ts
  50. 5
      npm/ng-packs/packages/core/src/lib/utils/internal-store-utils.ts
  51. 17
      npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html
  52. 5
      npm/ng-packs/packages/schematics/src/utils/type.ts
  53. 27
      npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.html
  54. 24
      npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html
  55. 65
      npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html
  56. 2
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.html
  57. 12
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html
  58. 31
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/grid-actions/grid-actions.component.html
  59. 26
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/page-toolbar/page-toolbar.component.html

18
docs/en/CLI.md

@ -41,6 +41,7 @@ Here, the list of all available commands before explaining their details:
* **`login`**: Authenticates on your computer with your [abp.io](https://abp.io/) username and password.
* **`logout`**: Logouts from your computer if you've authenticated before.
* **`build`**: Builds a GIT repository and depending repositories or a single .NET solution.
* **`bundle`**: Generates script and style references for an ABP Blazor project.
### help
@ -400,3 +401,20 @@ abp build --build-name "prod" --dotnet-build-arguments "\"--no-dependencies\""
For more details, see [build command documentation](CLI-BuildCommand.md).
#### bundle
This command generates script and style references for an ABP Blazor project and updates the **index.html** file. It helps developers to manage dependencies required by ABP modules easily. In order ```bundle``` command to work, its **executing directory** or passed ```--working-directory``` parameter's directory must contain a Blazor project file(*.csproj).
Usage:
````bash
abp bundle [options]
````
#### Options
* ```--working-directory``` or ```-wd```: Specifies the working directory. This option is useful when executing directory doesn't contain a Blazor project file.
* ```--force``` or ```-f```: Forces to build project before generating references.
For more details about managing style and script references in Blazor apps, see [Managing Global Scripts & Styles](UI/Blazor/Global-Scripts-Styles.md)

11
docs/en/Tutorials/Part-10.md

@ -911,6 +911,17 @@ You can run the application and try to create a new book or update an existing b
{{else if UI=="NG"}}
### Service Proxy Generation
Since the HTTP APIs have been changed, you need to update Angular client side [service proxies](../UI/Angular/Service-Proxies.md). Before running `generate-proxy` command, your host must be up and running.
Run the following command in the `angular` folder (you may need to stop the angular application):
```bash
abp generate-proxy
```
This command will update the service proxy files under the `/src/app/proxy/` folder.
### The Book List
Book list page change is trivial. Open the `/src/app/book/book.component.html` and add the following column definition between the `Name` and `Type` columns:

12
docs/en/Tutorials/Part-5.md

@ -423,13 +423,13 @@ Open the `/src/app/book/book.component.html` file and replace the create button
````html
<!-- Add the abpPermission directive -->
<button abpPermission="BookStore.Books.Create" id="create" class="btn btn-primary" type="button" (click)="createBook()">
<button *abpPermission="'BookStore.Books.Create'" id="create" class="btn btn-primary" type="button" (click)="createBook()">
<i class="fa fa-plus mr-1"></i>
<span>{%{{{ '::NewBook' | abpLocalization }}}%}</span>
</button>
````
* Just added `abpPermission="BookStore.Books.Create"` that hides the button if the current user has no permission.
* Just added `*abpPermission="'BookStore.Books.Create'"` that hides the button if the current user has no permission.
### Hide the Edit and Delete Actions
@ -443,18 +443,18 @@ Open the `/src/app/book/book.component.html` file and replace the edit and delet
````html
<!-- Add the abpPermission directive -->
<button abpPermission="BookStore.Books.Edit" ngbDropdownItem (click)="editBook(row.id)">
<button *abpPermission="'BookStore.Books.Edit'" ngbDropdownItem (click)="editBook(row.id)">
{%{{{ '::Edit' | abpLocalization }}}%}
</button>
<!-- Add the abpPermission directive -->
<button abpPermission="BookStore.Books.Delete" ngbDropdownItem (click)="delete(row.id)">
<button *abpPermission="'BookStore.Books.Delete'" ngbDropdownItem (click)="delete(row.id)">
{%{{{ '::Delete' | abpLocalization }}}%}
</button>
````
* Added `abpPermission="BookStore.Books.Edit"` that hides the edit action if the current user has no editing permission.
* Added `abpPermission="BookStore.Books.Delete"` that hides the delete action if the current user has no delete permission.
* Added `*abpPermission="'BookStore.Books.Edit'"` that hides the edit action if the current user has no editing permission.
* Added `*abpPermission="'BookStore.Books.Delete'"` that hides the delete action if the current user has no delete permission.
{{else if UI == "Blazor"}}

2
docs/en/UI/Angular/Permission-Management.md

@ -54,8 +54,6 @@ You can use the `PermissionDirective` to manage visibility of a DOM Element acco
As shown above you can remove elements from DOM with `abpPermission` structural directive.
The directive can also be used as an attribute directive but we recommend to you to use it as a structural directive.
## Permission Guard
You can use `PermissionGuard` if you want to control authenticated user's permission to access to the route during navigation.

2
docs/en/UI/Blazor/Customization-Overriding-Components.md

@ -46,7 +46,7 @@ Let's explain the code:
Now, you can run the application to see the result:
![bookstore-added-logo](D:/Github/abp/docs/en/images/bookstore-added-logo.png)
![bookstore-added-logo](../../images/bookstore-added-logo.png)
> Since the component inherits from the component it is replacing, you can use all the non-private fields/properties/methods of the base component in the derived component.

37
docs/en/UI/Blazor/Global-Scripts-Styles.md

@ -1,3 +1,38 @@
# Blazor UI: Managing Global Scripts & Styles
TODO
Some modules may require additional styles or scripts that need to be referenced in **index.html** file. It's not easy to find and update these types of references in Blazor apps. ABP offers a simple, powerful, and modular way to manage global style and scripts in Blazor apps.
To update script & style references without worrying about dependencies, ordering, etc in a project, you can use the [bundle command](../../CLI.md#bundle).
You can also add custom styles and scripts and let ABP manage them for you. In your Blazor project, you can create a class implementing `IBundleContributer` interface.
`IBundleContributer` interface contains two methods.
* `AddScripts(...)`
* `AddStyles(...)`
Both methods get `BundleContext` as a parameter. You can add scripts and styles to the `BundleContext` and run [bundle command](../../CLI.md#bundle). Bundle command detects custom styles and scripts with module dependencies and updates `index.html` file.
## Example Usage
```csharp
namespace MyProject.Blazor
{
public class MyProjectBundleContributer : IBundleContributer
{
public void AddScripts(BundleContext context)
{
context.Add("site.js");
}
public void AddStyles(BundleContext context)
{
context.Add("main.css");
context.Add("custom-styles.css");
}
}
}
```
> There is a BundleContributer class implementing `IBundleContributer` interface coming by default with the startup templates. So, most of the time, you don't need to add it manually.
> Bundle command adds style and script references individually. Bundling and minification support will be added to incoming releases.

37
docs/en/UI/Blazor/Message.md

@ -1,26 +1,28 @@
# Blazor: UI Message Service
# Blazor UI: Message Service
UI message service is used to show nice-looking messages to the user as a blocking dialog.
## Quick Example
Simply inject `IUiMessageService` to your page or component and call the `Success` method to show a success message.
Simply [inject](../../Dependency-Injection.md) `IUiMessageService` to your page or component and call the `Success` method to show a success message.
```csharp
namespace MyProject.Blazor.Pages
{
public partial class Index
{
public IUiMessageService UiMessageService { get; }
private readonly IUiMessageService _uiMessageService;
public Index(IUiMessageService uiMessageService)
{
UiMessageService = uiMessageService;
_uiMessageService = uiMessageService;
}
public async Task SaveAsync()
{
await UiMessageService.Success("Your changes have been successfully saved!", "Congratulations");
await _uiMessageService.Success(
"Your changes have been successfully saved!",
"Congratulations");
}
}
}
@ -30,7 +32,7 @@ It will show a dialog on the UI:
![blazor-message-success](../../images/blazor-message-success.png)
If you inherit your page or component from `AbpComponentBase` class, you can use the `Message` property to access the `IUiMessageService`.
If you inherit your page or component from the `AbpComponentBase` class, you can use the `Message` property to access the `IUiMessageService` as a pre-injected property.
```csharp
namespace MyProject.Blazor.Pages
@ -39,12 +41,14 @@ namespace MyProject.Blazor.Pages
{
public async Task SaveAsync()
{
await Message.Success("Your changes have been successfully saved!", "Congratulations");
await Message.Success(
"Your changes have been successfully saved!",
"Congratulations");
}
}
}
```
> You typically use `@inherits AbpComponentBase` in the `.razor` file to inherit from the `AbpComponentBase`, instead of inheriting in the code behind file.
## Informative Messages
@ -64,7 +68,7 @@ All of these methods get three parameters:
**Example: Show an error message**
````csharp
UiMessageService.Error('Your credit card number is not valid!');
_uiMessageService.Error('Your credit card number is not valid!');
````
![blazor-message-success](../../images/blazor-message-error.png)
@ -81,7 +85,7 @@ Use the following code to get a confirmation result from the user:
```csharp
public async Task DeleteAsync()
{
var confirmed = await UiMessageService.Confirm("Are you sure to delete the 'admin' role?");
var confirmed = await _uiMessageService.Confirm("Are you sure to delete the 'admin' role?");
if(confirmed)
{
//Delete the 'admin' role here.
@ -95,12 +99,15 @@ The resulting UI will be like shown below:
If the user has clicked the `Yes` button, the `Confirm` method's return value will be `true`.
## UI Message Configuration
## Configuration
It is easy to change default UI Message options if you like to customize messages. Provide an `action` to the `options` parameter and change the default values.
It is easy to change default message options if you like to it per message. Provide an `action` to the `options` parameter as shown below.
```csharp
await UiMessageService.Success("Your changes have been successfully saved!", "Congratulations", (options) =>
await _uiMessageService.Success(
"Your changes have been successfully saved!",
"Congratulations",
(options) =>
{
options.MessageIcon = "msg-icon-new";
options.CenterMessage = false;
@ -112,8 +119,8 @@ List of the options that you can change by providing the `action` parameter.
* `CenterMessage` : (Default: true) If true, the message dialogue will be centered on the screen.
* `ShowMessageIcon` : (Default: true) If true, the message dialogue will show the large icon for the current message type.
* `MessageIcon` : Overrides the build-in message icon.
* `OkButtonText` : Custom text for the Ok button.
* `OkButtonIcon` : Custom icon for the Ok button.
* `OkButtonText` : Custom text for the OK button.
* `OkButtonIcon` : Custom icon for the OK button.
* `ConfirmButtonText` : Custom text for the Confirmation button.
* `ConfirmButtonIcon` : Custom icon for the Confirmation button.
* `CancelButtonText` : Custom text for the Cancel button.

33
docs/en/UI/Blazor/Overall.md

@ -97,7 +97,7 @@ These libraries are selected as the base libraries and available to the applicat
The themes provide the layout. So, you have a responsive layout with the standard features already implemented. The screenshot below has taken from the layout of the [Basic Theme](Basic-Theme.md):
![basic-theme-application-layout](D:/Github/abp/docs/en/images/basic-theme-application-layout.png)
![basic-theme-application-layout](../../images/basic-theme-application-layout.png)
See the [Theming](Theming.md) document for more layout options and other details.
@ -121,6 +121,35 @@ ABP provides useful services that you can consume in your applications. Some of
* [ISettingProvider](Settings.md) is used to access to the current setting values.
* `ICurrentUser` and `ICurrentTenant` is used to get information about the current user and the tenant.
## Dependency Injection
Razor components doesn't support [constructor injection](../../Dependency-Injection.md) by default. ABP makes possible to inject dependencies into the constructor of the code-behind file of a component.
**Example: Constructor-inject a service in the code-behind file of a component**
````csharp
using Microsoft.AspNetCore.Components;
namespace MyProject.Blazor.Pages
{
public partial class Index
{
private readonly NavigationManager _navigationManager;
public Index(NavigationManager navigationManager)
{
_navigationManager = navigationManager;
}
}
}
````
ABP makes this possible by auto registering components to and resolving the component from the [Dependency Injection](../../Dependency-Injection.md) system.
> You can still continue to use property injection and the standard `[Inject]` approach if you prefer.
Resolving a component from the Dependency Injection system makes it possible to easily replace components of a depended module.
## Customization
While the theme and some modules come as NuGet packages, you can still override and customize them on need. See the [Customization / Overriding Components](Customization-Overriding-Components.md) document.
While the theme and some modules come as NuGet packages, you can still replace/override and customize them on need. See the [Customization / Overriding Components](Customization-Overriding-Components.md) document.

61
docs/en/UI/Blazor/Page-Alerts.md

@ -1,3 +1,62 @@
# Blazor UI: Page Alerts
TODO
It is common to show error, warning or information alerts to inform the user. An example *Service Interruption* alert is shown below:
![blazor-page-alert-example](../../images/blazor-page-alert-example.png)
## Quick Example
Simply [inject](../../Dependency-Injection.md) `IAlertManager` to your page or component and call the `Alerts.Warning` method to show a success message.
```csharp
namespace MyProject.Blazor.Pages
{
public partial class Index
{
private readonly IAlertManager _alertManager;
public Index(IAlertManager alertManager)
{
this._alertManager = alertManager;
}
protected override void OnInitialized()
{
_alertManager.Alerts.Warning(
"We will have a service interruption between 02:00 AM and 04:00 AM at October 23, 2023!",
"Service Interruption");
base.OnInitialized();
}
}
}
```
If you inherit your page or component from the `AbpComponentBase` class, you can use the `Alerts` property to add alerts.
```csharp
namespace MyProject.Blazor.Pages
{
public partial class Index : AbpComponentBase
{
protected override void OnInitialized()
{
Alerts.Warning(
"We will have a service interruption between 02:00 AM and 04:00 AM at October 23, 2023!",
"Service Interruption");
base.OnInitialized();
}
}
}
```
> You typically use `@inherits AbpComponentBase` in the `.razor` file to inherit from the `AbpComponentBase`, instead of inheriting in the code behind file.
### Alert Types
`Warning` is used to show a warning alert. Other common methods are `Info`, `Danger` and `Success`.
Beside the standard methods, you can use the `Alerts.Add` method by passing an `AlertType` `enum` with one of these values: `Default`, `Primary`, `Secondary`, `Success`, `Danger`, `Warning`, `Info`, `Light`, `Dark`.
### Dismissible
All alert methods gets an optional `dismissible` parameter. Default value is `true` which makes the alert box dismissible. Set it to `false` to create a sticky alert box.

2
docs/en/UI/Blazor/Theming.md

@ -44,7 +44,7 @@ All themes must define a layout for the application. The following image shows t
And the same page is shown below with the [Lepton Theme](https://commercial.abp.io/themes) application layout:
![lepton-theme-application-layout](../../images/lepton-theme-application-layout.png)
![lepton-theme-application-layout](../../images/lepton-theme-blazor-layout.png)
As you can see, the page is the same, but the look is completely different in the themes above.

BIN
docs/en/images/blazor-page-alert-example.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/en/images/lepton-theme-blazor-layout.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

3
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Alerts/AlertManager.cs

@ -1,4 +1,5 @@
using Volo.Abp.DependencyInjection;
using Volo.Abp.AspNetCore.Components.Alerts;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Components.WebAssembly.Alerts
{

7
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Alerts/IAlertManager.cs

@ -1,7 +0,0 @@
namespace Volo.Abp.AspNetCore.Components.WebAssembly.Alerts
{
public interface IAlertManager
{
AlertList Alerts { get; }
}
}

6
framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs

@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.AspNetCore.Components.Alerts;
using Volo.Abp.AspNetCore.Components.Messages;
using Volo.Abp.AspNetCore.Components.Notifications;
using Volo.Abp.Localization;
@ -61,6 +62,11 @@ namespace Volo.Abp.AspNetCore.Components
protected IUiNotificationService Notify => LazyGetNonScopedRequiredService(ref _notify);
private IUiNotificationService _notify;
protected IAlertManager AlertManager => LazyGetNonScopedRequiredService(ref _alertManager);
private IAlertManager _alertManager;
protected AlertList Alerts => AlertManager.Alerts;
protected IObjectMapper ObjectMapper
{
get

11
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Alerts/AlertList.cs → framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertList.cs

@ -1,6 +1,11 @@
using System.Collections.ObjectModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Components.WebAssembly.Alerts
namespace Volo.Abp.AspNetCore.Components.Alerts
{
public class AlertList : ObservableCollection<AlertMessage>
{
@ -29,4 +34,4 @@ namespace Volo.Abp.AspNetCore.Components.WebAssembly.Alerts
Add(new AlertMessage(AlertType.Success, text, title, dismissible));
}
}
}
}

4
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Alerts/AlertMessage.cs → framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertMessage.cs

@ -1,6 +1,6 @@
using JetBrains.Annotations;
namespace Volo.Abp.AspNetCore.Components.WebAssembly.Alerts
namespace Volo.Abp.AspNetCore.Components.Alerts
{
public class AlertMessage
{
@ -27,4 +27,4 @@ namespace Volo.Abp.AspNetCore.Components.WebAssembly.Alerts
Dismissible = dismissible;
}
}
}
}

10
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Alerts/AlertType.cs → framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/AlertType.cs

@ -1,4 +1,10 @@
namespace Volo.Abp.AspNetCore.Components.WebAssembly.Alerts
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Components.Alerts
{
public enum AlertType
{
@ -12,4 +18,4 @@
Light,
Dark
}
}
}

13
framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/Alerts/IAlertManager.cs

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Components.Alerts
{
public interface IAlertManager
{
AlertList Alerts { get; }
}
}

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs

@ -31,6 +31,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
public string AutocompleteFilterParamName { get; set; }
public string AutocompleteSelectedItemName { get; set; }
public string AutocompleteSelectedItemValue { get; set; }
public AbpSelectTagHelper(AbpSelectTagHelperService tagHelperService)
: base(tagHelperService)
{

12
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Diagnostics;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
@ -85,6 +86,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{
selectTagHelper.Items = GetSelectItems(context, output);
}
else
{
selectTagHelper.Items = new SelectListItem[]
{
new SelectListItem(TagHelper.AutocompleteSelectedItemName,TagHelper.AutocompleteSelectedItemValue,true)
};
}
var selectTagHelperOutput = await selectTagHelper.ProcessAndGetOutputAsync(GetInputAttributes(context, output), context, "select", TagMode.StartTagAndEndTag);
@ -107,6 +115,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
output.Attributes.Add("data-autocomplete-display-property", TagHelper.AutocompleteDisplayPropertyName);
output.Attributes.Add("data-autocomplete-value-property", TagHelper.AutocompleteValuePropertyName);
output.Attributes.Add("data-autocomplete-filter-param-name", TagHelper.AutocompleteFilterParamName);
output.Attributes.Add("data-autocomplete-selected-item-name", TagHelper.AutocompleteSelectedItemName);
output.Attributes.Add("data-autocomplete-selected-item-value", TagHelper.AutocompleteSelectedItemValue);
}
}

15
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js

@ -80,6 +80,18 @@
var displayValue = $(this).data("autocompleteValueProperty");
var itemsPropertyName = $(this).data("autocompleteItemsProperty");
var filterParamName = $(this).data("autocompleteFilterParamName");
var selectedText = $(this).data("autocompleteSelectedItemName");
var name = $(this).attr("name");
var selectedTextInputName = name.substring(0, name.length - 1) + "_Text]";
var selectedTextInput = $('<input>', {
type: 'hidden',
id: selectedTextInputName,
name: selectedTextInputName,
});
if (selectedText != "") {
selectedTextInput.val(selectedText);
}
selectedTextInput.insertAfter($select);
$select.select2({
ajax: {
url: url,
@ -109,6 +121,9 @@
},
width: '100%'
});
$select.on('select2:select', function (e) {
selectedTextInput.val(e.params.data.text);
});
});
}
}

3
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs

@ -140,8 +140,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
},
OnTable = new ExtensionPropertyUiTableDto
{
IsVisible = propertyConfig.UI.OnTable.IsVisible &&
propertyConfig.UI.Lookup.Url.IsNullOrEmpty()
IsVisible = propertyConfig.UI.OnTable.IsVisible
},
Lookup = new ExtensionPropertyUiLookupDto
{

2
framework/src/Volo.Abp.BlazoriseUI/Components/AlertWrapper.cs

@ -1,4 +1,4 @@
using Volo.Abp.AspNetCore.Components.WebAssembly.Alerts;
using Volo.Abp.AspNetCore.Components.Alerts;
namespace Volo.Abp.BlazoriseUI.Components
{

4
framework/src/Volo.Abp.BlazoriseUI/Components/PageAlert.razor.cs

@ -5,7 +5,7 @@ using System.Linq;
using Blazorise;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Routing;
using Volo.Abp.AspNetCore.Components.WebAssembly.Alerts;
using Volo.Abp.AspNetCore.Components.Alerts;
namespace Volo.Abp.BlazoriseUI.Components
{
@ -47,7 +47,7 @@ namespace Volo.Abp.BlazoriseUI.Components
{
Alerts.Add(new AlertWrapper
{
AlertMessage = (AspNetCore.Components.WebAssembly.Alerts.AlertMessage)item,
AlertMessage = (AspNetCore.Components.Alerts.AlertMessage)item,
IsVisible = true
});
}

14
framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextMySQLExtensions.cs

@ -14,11 +14,21 @@ namespace Volo.Abp.EntityFrameworkCore
{
if (context.ExistingConnection != null)
{
return context.DbContextOptions.UseMySql(context.ExistingConnection, ServerVersion.AutoDetect(context.ConnectionString), mySQLOptionsAction);
return context.DbContextOptions.UseMySql(context.ExistingConnection,
ServerVersion.AutoDetect(context.ConnectionString), optionsBuilder =>
{
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
mySQLOptionsAction?.Invoke(optionsBuilder);
});
}
else
{
return context.DbContextOptions.UseMySql(context.ConnectionString, ServerVersion.AutoDetect(context.ConnectionString), mySQLOptionsAction);
return context.DbContextOptions.UseMySql(context.ConnectionString,
ServerVersion.AutoDetect(context.ConnectionString), optionsBuilder =>
{
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
mySQLOptionsAction?.Invoke(optionsBuilder);
});
}
}
}

1
framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextOracleDevartExtensions.cs

@ -13,6 +13,7 @@
// [CanBeNull] Action<OracleDbContextOptionsBuilder> oracleOptionsAction = null,
// bool useExistingConnectionIfAvailable = false)
// {
// TODO: UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
// if (useExistingConnectionIfAvailable && context.ExistingConnection != null)
// {
// return context.DbContextOptions.UseOracle(context.ExistingConnection, oracleOptionsAction);

1
framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextOracleExtensions.cs

@ -12,6 +12,7 @@
// [NotNull] this AbpDbContextConfigurationContext context,
// [CanBeNull] Action<OracleDbContextOptionsBuilder> oracleOptionsAction = null)
// {
// TODO: UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
// if (context.ExistingConnection != null)
// {
// return context.DbContextOptions.UseOracle(context.ExistingConnection, oracleOptionsAction);

12
framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextPostgreSqlExtensions.cs

@ -22,11 +22,19 @@ namespace Volo.Abp.EntityFrameworkCore
{
if (context.ExistingConnection != null)
{
return context.DbContextOptions.UseNpgsql(context.ExistingConnection, postgreSqlOptionsAction);
return context.DbContextOptions.UseNpgsql(context.ExistingConnection, optionsBuilder =>
{
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
postgreSqlOptionsAction?.Invoke(optionsBuilder);
});
}
else
{
return context.DbContextOptions.UseNpgsql(context.ConnectionString, postgreSqlOptionsAction);
return context.DbContextOptions.UseNpgsql(context.ConnectionString, optionsBuilder =>
{
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
postgreSqlOptionsAction?.Invoke(optionsBuilder);
});
}
}
}

12
framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextSqlServerExtensions.cs

@ -14,11 +14,19 @@ namespace Volo.Abp.EntityFrameworkCore
{
if (context.ExistingConnection != null)
{
return context.DbContextOptions.UseSqlServer(context.ExistingConnection, sqlServerOptionsAction);
return context.DbContextOptions.UseSqlServer(context.ExistingConnection, optionsBuilder =>
{
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
sqlServerOptionsAction?.Invoke(optionsBuilder);
});
}
else
{
return context.DbContextOptions.UseSqlServer(context.ConnectionString, sqlServerOptionsAction);
return context.DbContextOptions.UseSqlServer(context.ConnectionString, optionsBuilder =>
{
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
sqlServerOptionsAction?.Invoke(optionsBuilder);
});
}
}
}

12
framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextSqliteExtensions.cs

@ -14,11 +14,19 @@ namespace Volo.Abp.EntityFrameworkCore
{
if (context.ExistingConnection != null)
{
return context.DbContextOptions.UseSqlite(context.ExistingConnection, sqliteOptionsAction);
return context.DbContextOptions.UseSqlite(context.ExistingConnection, optionsBuilder =>
{
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
sqliteOptionsAction?.Invoke(optionsBuilder);
});
}
else
{
return context.DbContextOptions.UseSqlite(context.ConnectionString, sqliteOptionsAction);
return context.DbContextOptions.UseSqlite(context.ConnectionString, optionsBuilder =>
{
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
sqliteOptionsAction?.Invoke(optionsBuilder);
});
}
}
}

3
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs

@ -31,12 +31,11 @@ namespace Volo.Abp.Http.Modeling
}
public static ActionApiDescriptionModel Create([NotNull] string uniqueName, [NotNull] MethodInfo method, [NotNull] string url, [NotNull] string httpMethod, [NotNull] IList<string> supportedVersions)
public static ActionApiDescriptionModel Create([NotNull] string uniqueName, [NotNull] MethodInfo method, [NotNull] string url, [CanBeNull] string httpMethod, [NotNull] IList<string> supportedVersions)
{
Check.NotNull(uniqueName, nameof(uniqueName));
Check.NotNull(method, nameof(method));
Check.NotNull(url, nameof(url));
Check.NotNull(httpMethod, nameof(httpMethod));
Check.NotNull(supportedVersions, nameof(supportedVersions));
return new ActionApiDescriptionModel

18
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/EntityExtensionConfiguration.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using JetBrains.Annotations;
using Volo.Abp.Localization;
namespace Volo.Abp.ObjectExtending.Modularity
{
@ -47,14 +48,29 @@ namespace Volo.Abp.ObjectExtending.Modularity
propertyName,
() => new ExtensionPropertyConfiguration(this, propertyType, propertyName)
);
configureAction?.Invoke(propertyInfo);
NormalizeProperty(propertyInfo);
if (!propertyInfo.UI.Lookup.Url.IsNullOrEmpty())
{
AddLookupTextProperty(propertyInfo);
propertyInfo.UI.OnTable.IsVisible = false;
propertyInfo.Api.OnGet.IsAvailable = false;
}
return this;
}
private void AddLookupTextProperty(ExtensionPropertyConfiguration propertyInfo)
{
var lookupTextPropertyName = $"{propertyInfo.Name}_Text";
var lookupTextPropertyInfo = Properties.GetOrAdd(
lookupTextPropertyName,
() => new ExtensionPropertyConfiguration(this, typeof(string), lookupTextPropertyName)
);
lookupTextPropertyInfo.DisplayName = propertyInfo.DisplayName ?? new FixedLocalizableString(propertyInfo.Name);
}
[NotNull]
public virtual ImmutableList<ExtensionPropertyConfiguration> GetProperties()
{

1
modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/AbpAuditLoggingEfCoreQueryableExtensions.cs

@ -15,7 +15,6 @@ namespace Volo.Abp.AuditLogging
}
return queryable
.AsSplitQuery()
.Include(x => x.Actions)
.Include(x => x.EntityChanges).ThenInclude(ec=>ec.PropertyChanges);
}

6
modules/docs/app/VoloDocs.Web/Utils/GlobalExceptionHandlerMiddleware.cs

@ -11,9 +11,9 @@ namespace VoloDocs.Web.Utils
public class GlobalExceptionHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<AbpUnitOfWorkMiddleware> _logger;
private readonly ILogger<GlobalExceptionHandlerMiddleware> _logger;
public GlobalExceptionHandlerMiddleware(RequestDelegate next, ILogger<AbpUnitOfWorkMiddleware> logger)
public GlobalExceptionHandlerMiddleware(RequestDelegate next, ILogger<GlobalExceptionHandlerMiddleware> logger)
{
_logger = logger;
_next = next;
@ -42,4 +42,4 @@ namespace VoloDocs.Web.Utils
}
}
}
}
}

1
modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityEfCoreQueryableExtensions.cs

@ -13,7 +13,6 @@ namespace Volo.Abp.Identity.EntityFrameworkCore
}
return queryable
.AsSplitQuery()
.Include(x => x.Roles)
.Include(x => x.Logins)
.Include(x => x.Claims)

37
modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/CreateModal.cshtml

@ -24,23 +24,28 @@
@foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties<CreateModalModel.RoleInfoModel>())
{
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
if (!propertyInfo.Name.EndsWith("_Text"))
{
<abp-select asp-for="Role.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="Role.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.Role.ExtraProperties[propertyInfo.Name])" />
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
{
<abp-select asp-for="Role.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-selected-item-name="@Model.Role.ExtraProperties[propertyInfo.Name+"_Text"]"
autocomplete-selected-item-value="@Model.Role.ExtraProperties[propertyInfo.Name]"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="Role.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.Role.ExtraProperties[propertyInfo.Name])" />
}
}
}
</abp-modal-body>

37
modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/EditModal.cshtml

@ -35,23 +35,28 @@
@foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties<CreateModalModel.RoleInfoModel>())
{
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
if (!propertyInfo.Name.EndsWith("_Text"))
{
<abp-select asp-for="Role.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="Role.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.Role.ExtraProperties[propertyInfo.Name])" />
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
{
<abp-select asp-for="Role.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-selected-item-name="@Model.Role.ExtraProperties[propertyInfo.Name+"_Text"]"
autocomplete-selected-item-value="@Model.Role.ExtraProperties[propertyInfo.Name]"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="Role.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.Role.ExtraProperties[propertyInfo.Name])" />
}
}
}
</abp-modal-body>

37
modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml

@ -29,23 +29,28 @@
@foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties<CreateModalModel.UserInfoViewModel>())
{
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
if (!propertyInfo.Name.EndsWith("_Text"))
{
<abp-select asp-for="UserInfo.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="UserInfo.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.UserInfo.ExtraProperties[propertyInfo.Name])" />
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
{
<abp-select asp-for="UserInfo.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-selected-item-name="@Model.UserInfo.ExtraProperties[propertyInfo.Name+"_Text"]"
autocomplete-selected-item-value="@Model.UserInfo.ExtraProperties[propertyInfo.Name]"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="UserInfo.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.UserInfo.ExtraProperties[propertyInfo.Name])" />
}
}
}
</abp-tab>

37
modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml

@ -31,23 +31,28 @@
@foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties<EditModalModel.UserInfoViewModel>())
{
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
if (!propertyInfo.Name.EndsWith("_Text"))
{
<abp-select asp-for="UserInfo.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="UserInfo.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.UserInfo.ExtraProperties[propertyInfo.Name])" />
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
{
<abp-select asp-for="UserInfo.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-selected-item-name="@Model.UserInfo.ExtraProperties[propertyInfo.Name+"_Text"]"
autocomplete-selected-item-value="@Model.UserInfo.ExtraProperties[propertyInfo.Name]"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="UserInfo.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.UserInfo.ExtraProperties[propertyInfo.Name])" />
}
}
}

2
modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/AbpIdentityServerEfCoreQueryableExtensions.cs

@ -17,7 +17,6 @@ namespace Volo.Abp.IdentityServer
}
return queryable
.AsSplitQuery()
.Include(x => x.Secrets)
.Include(x => x.UserClaims)
.Include(x => x.Scopes)
@ -57,7 +56,6 @@ namespace Volo.Abp.IdentityServer
}
return queryable
.AsSplitQuery()
.Include(x => x.AllowedGrantTypes)
.Include(x => x.RedirectUris)
.Include(x => x.PostLogoutRedirectUris)

37
modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml

@ -24,23 +24,28 @@
@foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties<CreateModalModel.TenantInfoModel>())
{
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
if (!propertyInfo.Name.EndsWith("_Text"))
{
<abp-select asp-for="Tenant.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="Tenant.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.Tenant.ExtraProperties[propertyInfo.Name])" />
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
{
<abp-select asp-for="Tenant.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-selected-item-name="@Model.Tenant.ExtraProperties[propertyInfo.Name+"_Text"]"
autocomplete-selected-item-value="@Model.Tenant.ExtraProperties[propertyInfo.Name]"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="Tenant.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.Tenant.ExtraProperties[propertyInfo.Name])" />
}
}
}
</abp-modal-body>

37
modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml

@ -20,23 +20,28 @@
<abp-input asp-for="Tenant.Name" label="@L["TenantName"].Value" />
@foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties<EditModalModel.TenantInfoModel>())
{
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
if (!propertyInfo.Name.EndsWith("_Text"))
{
<abp-select asp-for="Tenant.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="Tenant.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.Tenant.ExtraProperties[propertyInfo.Name])" />
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
{
<abp-select asp-for="Tenant.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-selected-item-name="@Model.Tenant.ExtraProperties[propertyInfo.Name+"_Text"]"
autocomplete-selected-item-value="@Model.Tenant.ExtraProperties[propertyInfo.Name]"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="Tenant.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.Tenant.ExtraProperties[propertyInfo.Name])" />
}
}
}
</abp-modal-body>

2
npm/ng-packs/angular.json

@ -378,7 +378,6 @@
"polyfills": "apps/dev-app/src/polyfills.ts",
"tsConfig": "apps/dev-app/tsconfig.dev.json",
"aot": true,
"extractCss": true,
"allowedCommonJsDependencies": ["chart.js", "js-sha256"],
"assets": ["apps/dev-app/src/favicon.ico", "apps/dev-app/src/assets"],
"styles": [
@ -433,7 +432,6 @@
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,

47
npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts

@ -1,59 +1,64 @@
import {
ChangeDetectorRef,
Directive,
ElementRef,
Input,
OnChanges,
OnDestroy,
OnInit,
Optional,
Renderer2,
SimpleChanges,
TemplateRef,
ViewContainerRef,
} from '@angular/core';
import { Subscription } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';
import { PermissionService } from '../services/permission.service';
@Directive({
selector: '[abpPermission]',
})
export class PermissionDirective implements OnInit, OnDestroy, OnChanges {
export class PermissionDirective implements OnDestroy, OnChanges {
@Input('abpPermission') condition: string;
subscription: Subscription;
constructor(
private elRef: ElementRef,
private elRef: ElementRef<HTMLElement>,
private renderer: Renderer2,
@Optional() private templateRef: TemplateRef<any>,
private vcRef: ViewContainerRef,
private permissionService: PermissionService,
private cdRef: ChangeDetectorRef,
) {}
private check() {
if (this.subscription) {
this.subscription.unsubscribe();
}
this.subscription = this.permissionService
.getGrantedPolicy$(this.condition)
.pipe(distinctUntilChanged())
.subscribe(isGranted => {
if (this.templateRef && isGranted) {
this.vcRef.clear();
this.vcRef.createEmbeddedView(this.templateRef);
} else if (this.templateRef && !isGranted) {
this.vcRef.clear();
} else if (!isGranted && !this.templateRef) {
this.renderer.removeChild(
(this.elRef.nativeElement as HTMLElement).parentElement,
this.elRef.nativeElement,
);
}
if (this.templateRef) this.initStructural(isGranted);
else this.initAttribute(isGranted);
this.cdRef.detectChanges();
});
}
ngOnInit() {
if (this.templateRef && !this.condition) {
this.vcRef.createEmbeddedView(this.templateRef);
private initStructural(isGranted: boolean) {
this.vcRef.clear();
if (isGranted) this.vcRef.createEmbeddedView(this.templateRef);
}
/**
* @deprecated Will be deleted in v5.0
*/
private initAttribute(isGranted: boolean) {
if (!isGranted) {
this.renderer.removeChild(this.elRef.nativeElement.parentElement, this.elRef.nativeElement);
}
}
@ -61,9 +66,7 @@ export class PermissionDirective implements OnInit, OnDestroy, OnChanges {
if (this.subscription) this.subscription.unsubscribe();
}
ngOnChanges({ condition }: SimpleChanges) {
if ((condition || { currentValue: null }).currentValue) {
this.check();
}
ngOnChanges() {
this.check();
}
}

10
npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts

@ -9,11 +9,7 @@ import { RestService } from './rest.service';
providedIn: 'root',
})
export class ApplicationConfigurationService {
get apiName(): string {
return this.environment.getEnvironment().application?.name;
}
constructor(private rest: RestService, private environment: EnvironmentService) {}
constructor(private rest: RestService) {}
getConfiguration(): Observable<ApplicationConfiguration.Response> {
const request: Rest.Request<null> = {
@ -21,8 +17,6 @@ export class ApplicationConfigurationService {
url: '/api/abp/application-configuration',
};
return this.rest.request<null, ApplicationConfiguration.Response>(request, {
apiName: this.apiName,
});
return this.rest.request<null, ApplicationConfiguration.Response>(request, {});
}
}

2
npm/ng-packs/packages/core/src/lib/services/config-state.service.ts

@ -15,7 +15,7 @@ export class ConfigStateService {
}
setState = (state: ApplicationConfiguration.Response) => {
this.store.patch(state);
this.store.set(state);
};
getOne$(key: string) {

2
npm/ng-packs/packages/core/src/lib/services/environment.service.ts

@ -31,6 +31,6 @@ export class EnvironmentService {
}
setState(environment: Environment) {
this.store.patch(environment);
this.store.set(environment);
}
}

18
npm/ng-packs/packages/core/src/lib/services/permission.service.ts

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators';
import { map, tap } from 'rxjs/operators';
import snq from 'snq';
import { ApplicationConfiguration } from '../models/application-configuration';
import { ConfigStateService } from './config-state.service';
@ -9,7 +9,9 @@ export class PermissionService {
constructor(private configState: ConfigStateService) {}
getGrantedPolicy$(key: string) {
return this.getStream().pipe(map(policies => this.isPolicyGranted(key, policies)));
return this.getStream().pipe(
map(grantedPolicies => this.isPolicyGranted(key, grantedPolicies)),
);
}
getGrantedPolicy(key: string) {
@ -17,7 +19,7 @@ export class PermissionService {
return this.isPolicyGranted(key, policies);
}
private isPolicyGranted(key: string, policies: ApplicationConfiguration.Policy) {
private isPolicyGranted(key: string, grantedPolicies: ApplicationConfiguration.Policy) {
if (!key) return true;
const orRegexp = /\|\|/g;
@ -29,16 +31,16 @@ export class PermissionService {
if (keys.length < 2) return false;
return keys.some(k => this.getPolicy(k.trim(), policies));
return keys.some(k => this.getPolicy(k.trim(), grantedPolicies));
} else if (andRegexp.test(key)) {
const keys = key.split('&&').filter(Boolean);
if (keys.length < 2) return false;
return keys.every(k => this.getPolicy(k.trim(), policies));
return keys.every(k => this.getPolicy(k.trim(), grantedPolicies));
}
return this.getPolicy(key, policies);
return this.getPolicy(key, grantedPolicies);
}
private getStream() {
@ -53,7 +55,7 @@ export class PermissionService {
return snq(() => applicationConfiguration.auth.grantedPolicies);
}
private getPolicy(policy: string, policies: ApplicationConfiguration.Policy) {
return snq(() => policies[policy], false);
private getPolicy(key: string, grantedPolicies: ApplicationConfiguration.Policy) {
return snq(() => grantedPolicies[key], false);
}
}

5
npm/ng-packs/packages/core/src/lib/utils/internal-store-utils.ts

@ -30,6 +30,11 @@ export class InternalStore<State> {
this.update$.next(state);
}
set(state: State) {
this.state$.next(state);
this.update$.next(state);
}
reset() {
this.patch(this.initialState);
}

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

@ -51,14 +51,15 @@
>
{{ 'AbpIdentity::Permissions' | abpLocalization }}
</button>
<button
*ngIf="!row.isStatic"
[abpPermission]="'AbpIdentity.Roles.Delete'"
ngbDropdownItem
(click)="delete(row.id, row.name)"
>
{{ 'AbpIdentity::Delete' | abpLocalization }}
</button>
<ng-container *ngIf="!row.isStatic">
<button
*abpPermission="'AbpIdentity.Roles.Delete'"
ngbDropdownItem
(click)="delete(row.id, row.name)"
>
{{ 'AbpIdentity::Delete' | abpLocalization }}
</button>
</ng-container>
</div>
</div>
</ng-template>

5
npm/ng-packs/packages/schematics/src/utils/type.ts

@ -9,6 +9,11 @@ import { parseGenerics } from './tree';
export function createTypeSimplifier() {
const parseType = createTypeParser(type => {
const regexp = new RegExp(/.*(?<=\.)(?<generic>.+)<.*(?<=[\.<])(?<genericType>.+)>/gm);
const { generic, genericType } = regexp.exec(type)?.groups ?? {};
if (generic) return `${generic}<${genericType}>`;
type = type.replace(
/System\.([0-9A-Za-z.]+)/g,
(_, match) => SYSTEM_TYPES.get(match) ?? strings.camelize(match),

27
npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.html

@ -16,20 +16,21 @@
<div class="row">
<div class="col-12 col-md-3">
<ul class="nav flex-column nav-pills" id="nav-tab" role="tablist">
<li
*abpFor="let setting of settings; trackBy: trackByFn"
(click)="selected = setting"
class="nav-item pointer"
[abpPermission]="setting.requiredPolicy"
>
<a
class="nav-link"
[id]="setting.name + '-tab'"
role="tab"
[class.active]="setting.name === selected.name"
>{{ setting.name | abpLocalization }}</a
<ng-container *abpFor="let setting of settings; trackBy: trackByFn">
<li
(click)="selected = setting"
class="nav-item pointer"
*abpPermission="setting.requiredPolicy"
>
</li>
<a
class="nav-link"
[id]="setting.name + '-tab'"
role="tab"
[class.active]="setting.name === selected.name"
>{{ setting.name | abpLocalization }}</a
>
</li>
</ng-container>
</ul>
</div>
<div class="col-12 col-md-9">

24
npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html

@ -1,18 +1,16 @@
<ul class="navbar-nav">
<ng-container *ngFor="let item of navItems.items$ | async; trackBy: trackByFn">
<li
class="nav-item d-flex align-items-center"
*ngIf="item.visible()"
[abpPermission]="item.requiredPolicy"
>
<ng-container
*ngIf="item.component; else htmlTemplate"
[ngComponentOutlet]="item.component"
></ng-container>
<ng-container *ngIf="item.visible()">
<li class="nav-item d-flex align-items-center" *abpPermission="item.requiredPolicy">
<ng-container
*ngIf="item.component; else htmlTemplate"
[ngComponentOutlet]="item.component"
></ng-container>
<ng-template #htmlTemplate>
<div [innerHTML]="item.html" (click)="item.action ? item.action() : null"></div>
</ng-template>
</li>
<ng-template #htmlTemplate>
<div [innerHTML]="item.html" (click)="item.action ? item.action() : null"></div>
</ng-template>
</li>
</ng-container>
</ng-container>
</ul>

65
npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html

@ -16,37 +16,40 @@
</ng-template>
<ng-template #dropdownLink let-route>
<li
#navbarRootDropdown
class="nav-item dropdown"
display="static"
*ngIf="route.children?.length"
[abpPermission]="route.requiredPolicy"
(click)="
navbarRootDropdown.expand
? (navbarRootDropdown.expand = false)
: (navbarRootDropdown.expand = true)
"
>
<a
class="nav-link dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
href="javascript:void(0)"
>
<i *ngIf="route.iconClass" [ngClass]="route.iconClass"></i>
{{ route.name | abpLocalization }}
</a>
<div
#routeContainer
class="dropdown-menu border-0 shadow-sm"
(click)="$event.preventDefault(); $event.stopPropagation()"
[class.d-block]="smallScreen && navbarRootDropdown.expand"
<ng-container *ngIf="route.children?.length">
<li
#navbarRootDropdown
class="nav-item dropdown"
display="static"
*abpPermission="route.requiredPolicy"
(click)="
navbarRootDropdown.expand
? (navbarRootDropdown.expand = false)
: (navbarRootDropdown.expand = true)
"
>
<ng-container *ngTemplateOutlet="forTemplate; context: { $implicit: route }"></ng-container>
</div>
</li>
<a
class="nav-link dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
href="javascript:void(0)"
>
<i *ngIf="route.iconClass" [ngClass]="route.iconClass"></i>
{{ route.name | abpLocalization }}
</a>
<div
#routeContainer
class="dropdown-menu border-0 shadow-sm"
(click)="$event.preventDefault(); $event.stopPropagation()"
[class.d-block]="smallScreen && navbarRootDropdown.expand"
>
<ng-container
*ngTemplateOutlet="forTemplate; context: { $implicit: route }"
></ng-container>
</div>
</li>
</ng-container>
</ng-template>
<ng-template #forTemplate let-route>
@ -74,7 +77,7 @@
#dropdownSubmenu="ngbDropdown"
placement="right-top"
[autoClose]="true"
[abpPermission]="child.requiredPolicy"
*abpPermission="child.requiredPolicy"
>
<div ngbDropdownToggle [class.dropdown-toggle]="false">
<a

2
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.html

@ -1,4 +1,4 @@
<div class="form-group" [abpPermission]="prop.permission" [ngSwitch]="getComponent(prop)">
<div class="form-group" *abpPermission="prop.permission" [ngSwitch]="getComponent(prop)">
<ng-template ngSwitchCase="input">
<label [htmlFor]="prop.id">{{ prop.displayName | abpLocalization }} {{ asterisk }}</label>
<input

12
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html

@ -11,11 +11,7 @@
*ngTemplateOutlet="actionsTemplate || gridActions; context: { $implicit: row, index: i }"
></ng-container>
<ng-template #gridActions>
<abp-grid-actions
[index]="i"
[record]="row"
text="AbpUi::Actions"
></abp-grid-actions>
<abp-grid-actions [index]="i" [record]="row" text="AbpUi::Actions"></abp-grid-actions>
</ng-template>
</ng-template>
</ngx-datatable-column>
@ -28,11 +24,13 @@
[sortable]="prop.sortable"
>
<ng-template let-row="row" let-i="index" ngx-datatable-cell-template>
<ng-container [abpPermission]="prop.permission">
<ng-container *abpPermission="prop.permission">
<div
*ngIf="row['_' + prop.name].visible"
[innerHTML]="row['_' + prop.name].value | async"
(click)="prop.action && prop.action({ getInjected: getInjected, record: row, index: i })"
(click)="
prop.action && prop.action({ getInjected: getInjected, record: row, index: i })
"
[class.pointer]="prop.action"
></div>
</ng-container>

31
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/grid-actions/grid-actions.component.html

@ -24,19 +24,20 @@
></ng-container>
<ng-template #btnItem let-action>
<button
*ngIf="action.visible(data)"
ngbDropdownItem
[abpPermission]="action.permission"
(click)="action.action(data)"
type="button"
class="{{ actionList.length === 1 ? 'btn btn-primary' : '' }}"
[class.text-center]="actionList.length === 1"
>
<i [ngClass]="action.icon" [class.mr-1]="action.icon"></i>
<span *ngIf="action.icon; else ellipsis">{{ action.text | abpLocalization }}</span>
<ng-template #ellipsis>
<div abpEllipsis>{{ action.text | abpLocalization }}</div>
</ng-template>
</button>
<ng-container *ngIf="action.visible(data)">
<button
ngbDropdownItem
*abpPermission="action.permission"
(click)="action.action(data)"
type="button"
class="{{ actionList.length === 1 ? 'btn btn-primary' : '' }}"
[class.text-center]="actionList.length === 1"
>
<i [ngClass]="action.icon" [class.mr-1]="action.icon"></i>
<span *ngIf="action.icon; else ellipsis">{{ action.text | abpLocalization }}</span>
<ng-template #ellipsis>
<div abpEllipsis>{{ action.text | abpLocalization }}</div>
</ng-template>
</button>
</ng-container>
</ng-template>

26
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/page-toolbar/page-toolbar.component.html

@ -1,18 +1,20 @@
<div class="row justify-content-end mx-n1" id="AbpContentToolbar">
<div class="col-auto px-1 pt-2" *ngFor="let action of actionList; trackBy: trackByFn">
<ng-container *ngIf="action.visible(data)" [abpPermission]="action.permission">
<ng-container *ngIf="action.component as component; else button">
<ng-container
*ngComponentOutlet="component; injector: createInjector(action)"
></ng-container>
</ng-container>
<ng-container *ngIf="action.visible(data)">
<ng-container *abpPermission="action.permission">
<ng-container *ngIf="action.component as component; else button">
<ng-container
*ngComponentOutlet="component; injector: createInjector(action)"
></ng-container>
</ng-container>
<ng-template #button>
<button (click)="action.action(data)" type="button" class="btn btn-primary btn-sm">
<i [ngClass]="action.icon" [class.mr-1]="action.icon"></i>
{{ action.text | abpLocalization }}
</button>
</ng-template>
<ng-template #button>
<button (click)="action.action(data)" type="button" class="btn btn-primary btn-sm">
<i [ngClass]="action.icon" [class.mr-1]="action.icon"></i>
{{ action.text | abpLocalization }}
</button>
</ng-template>
</ng-container>
</ng-container>
</div>
</div>

Loading…
Cancel
Save