Browse Source

Merge branch 'dev' into pr/22004

pull/22004/head
maliming 1 year ago
parent
commit
827bbe4625
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 8
      Directory.Packages.props
  2. 5
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  3. 2
      docs/en/Community-Articles/2024-12-01-OpenAI-Integration/POST.md
  4. 327
      docs/en/Community-Articles/2025-01-24-Understanding-Transactions-in-ABP-Unit-Of-Work/POST.md
  5. BIN
      docs/en/Community-Articles/2025-01-24-Understanding-Transactions-in-ABP-Unit-Of-Work/pic.png
  6. 11
      docs/en/cli/index.md
  7. 8
      docs/en/docs-nav.json
  8. 15
      docs/en/framework/ui/blazor/overall.md
  9. 2
      docs/en/framework/ui/blazor/theming.md
  10. 8
      docs/en/framework/ui/mvc-razor-pages/tag-helpers/form-elements.md
  11. 2
      docs/en/modules/account/idle-session-timeout.md
  12. 106
      docs/en/samples/index.md
  13. 2
      docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step3-deployment-github-action.md
  14. 217
      docs/en/solution-templates/layered-web-application/deployment/deployment-docker-compose.md
  15. 4
      docs/en/solution-templates/layered-web-application/solution-structure.md
  16. 2
      docs/en/studio/monitoring-applications.md
  17. 8
      docs/en/studio/running-applications.md
  18. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-ordering-swagger-ui-in-browser.png
  19. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-swagger-ui-create-order-execute.png
  20. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-swagger-ui-create-product-execute.png
  21. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-swagger-ui-create-product-try.png
  22. BIN
      docs/en/tutorials/modular-crm/images/abp-studio-swagger-ui-in-browser.png
  23. 7
      docs/en/tutorials/modular-crm/part-03.md
  24. 16
      docs/en/tutorials/modular-crm/part-05.md
  25. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs
  26. 15
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs
  27. 3
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/TypeApiDescriptionModel.cs
  28. 4
      modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
  29. 24
      modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/wwwroot/themes/basic/layout.css
  30. 17
      modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/wwwroot/themes/basic/logo.svg
  31. 6
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Users/CmsUserDto.cs
  32. 2
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs
  33. 58
      modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs
  34. 15
      modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs
  35. 26
      npm/ng-packs/packages/schematics/src/utils/service.ts
  36. 10
      npm/ng-packs/packages/schematics/src/utils/type.ts
  37. 4
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj
  38. 4
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj
  39. 4
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj
  40. 4
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyCompanyName.MyProjectName.Blazor.Client.csproj
  41. 4
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj
  42. 4
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj
  43. 4
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Client.csproj
  44. 4
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client.csproj
  45. 4
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.csproj
  46. 4
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyCompanyName.MyProjectName.Blazor.WebApp.csproj
  47. 4
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/MyCompanyName.MyProjectName.Blazor.Host.Client.csproj
  48. 4
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj

8
Directory.Packages.props

@ -18,10 +18,10 @@
<PackageVersion Include="BunnyCDN.Net.Storage" Version="1.0.4" />
<PackageVersion Include="Azure.Messaging.ServiceBus" Version="7.18.1" />
<PackageVersion Include="Azure.Storage.Blobs" Version="12.22.1" />
<PackageVersion Include="Blazorise" Version="1.7.2" />
<PackageVersion Include="Blazorise.Components" Version="1.7.2" />
<PackageVersion Include="Blazorise.DataGrid" Version="1.7.2" />
<PackageVersion Include="Blazorise.Snackbar" Version="1.7.2" />
<PackageVersion Include="Blazorise" Version="1.7.3" />
<PackageVersion Include="Blazorise.Components" Version="1.7.3" />
<PackageVersion Include="Blazorise.DataGrid" Version="1.7.3" />
<PackageVersion Include="Blazorise.Snackbar" Version="1.7.3" />
<PackageVersion Include="Castle.Core" Version="5.1.1" />
<PackageVersion Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
<PackageVersion Include="CommonMark.NET" Version="0.15.1" />

5
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -683,6 +683,9 @@
"DiscountRate": "Discount Rate",
"Menu:RedisManagement": "Redis Management",
"RedisManagement": "Redis Management",
"Permission:RedisManagement": "Redis Management"
"Permission:RedisManagement": "Redis Management",
"UserCleanUp": "User Clean Up",
"Permission:UserCleanUp": "User Clean Up",
"AllowPrivateQuestion": "Allow Private Question"
}
}

2
docs/en/Community-Articles/2024-12-01-OpenAI-Integration/POST.md

@ -62,6 +62,8 @@ dotnet add package Microsoft.Extensions.AI.OpenAI --prerelease
> Replace the value of the `Key` with your OpenAI API key.
> **Important Security Note**: Storing sensitive information like API keys in `appsettings.json` is not recommended due to security concerns. Please refer to the [official Microsoft documentation](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets) for secure secret management best practices.
Next, add the following code to the `ConfigureServices` method in `OpenAIIntegrationBlazorModule`:
```csharp

327
docs/en/Community-Articles/2025-01-24-Understanding-Transactions-in-ABP-Unit-Of-Work/POST.md

@ -0,0 +1,327 @@
# Understanding Transactions in ABP Unit of Work
[The Unit of Work](https://en.wikipedia.org/wiki/Unit_of_work) is a software design pattern that maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems to ensure that all changes are made within a single transaction.
![pic](./pic.png)
## Transaction Management Overview
One of the primary responsibilities of the Unit of Work is managing database transactions. It provides the following transaction management features:
- Automatically manages database connections and transaction scopes, eliminating the need for manual transaction control
- Ensures business operation integrity by making all database operations within a unit of work either succeed or roll back completely
- Supports configuration of transaction isolation levels and timeout periods
- Supports nested transactions and transaction propagation
## Transaction Behavior
### Default Transaction Settings
You can modify the default behavior through the following configuration:
```csharp
Configure<AbpUnitOfWorkDefaultOptions>(options =>
{
/*
Modify the default transaction behavior for all unit of work:
- UnitOfWorkTransactionBehavior.Enabled: Always enable transactions, all requests will start a transaction
- UnitOfWorkTransactionBehavior.Disabled: Always disable transactions, no requests will start a transaction
- UnitOfWorkTransactionBehavior.Auto: Automatically decide whether to start a transaction based on HTTP request type
*/
options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled;
// Set default timeout
options.Timeout = TimeSpan.FromSeconds(30);
// Set default isolation level
options.IsolationLevel = IsolationLevel.ReadCommitted;
});
```
### Automatic Transaction Management
ABP Framework implements automatic management of Unit of Work and transactions through middlewares, MVC global filters, and interceptors. In most cases, you don't need to manage them manually
### Transaction Behavior for HTTP Requests
By default, the framework adopts an intelligent transaction management strategy for HTTP requests:
- `GET` requests won't start a transactional unit of work because there is no data modification
- Other HTTP requests (`POST/PUT/DELETE` etc.) will start a transactional unit of work
### Manual Transaction Control
If you need to manually start a new unit of work, you can customize whether to start a transaction and set the transaction isolation level and timeout:
```csharp
// Start a transactional unit of work
using (var uow = _unitOfWorkManager.Begin(
isTransactional: true,
isolationLevel: IsolationLevel.RepeatableRead,
timeout: 30
))
{
// Execute database operations within transaction
await uow.CompleteAsync();
}
```
```csharp
// Start a non-transactional unit of work
using (var uow = _unitOfWorkManager.Begin(
isTransactional: false
))
{
// Execute database operations without transaction
await uow.CompleteAsync();
}
```
### Configuring Transactions Using `[UnitOfWork]` Attribute
You can customize transaction behavior by using the `UnitOfWorkAttribute` on methods, classes, or interfaces:
```csharp
[UnitOfWork(
IsTransactional = true,
IsolationLevel = IsolationLevel.RepeatableRead,
Timeout = 30
)]
public virtual async Task ProcessOrderAsync(int orderId)
{
// Execute database operations within transaction
}
```
### Non-Transactional Unit of Work
In some scenarios, you might not need transaction support. You can create a non-transactional unit of work by setting `IsTransactional = false`:
```csharp
public virtual async Task ImportDataAsync(List<DataItem> items)
{
using (var uow = _unitOfWorkManager.Begin(
isTransactional: false
))
{
foreach (var item in items)
{
await _repository.InsertAsync(item, autoSave: true);
// Each InsertAsync will save to database immediately
// If subsequent operations fail, saved data won't be rolled back
}
await uow.CompleteAsync();
}
}
```
Applicable scenarios:
- Batch import data scenarios where partial success is accepted
- Read-only operations, such as queries
- Scenarios with low data consistency requirements
### Methods to Commit Transactions
#### In Transactional Unit of Work
A Unit of Work provides several methods to commit changes to the database:
1. **IUnitOfWork.SaveChangesAsync**
```csharp
await _unitOfWorkManager.Current.SaveChangesAsync();
```
2. **autoSave parameter in repositories**
```csharp
await _repository.InsertAsync(entity, autoSave: true);
```
Both `autoSave` and `SaveChangesAsync` commit changes in the current context to the database. However, these are not applied until `CompleteAsync` is called. If the unit of work throws an exception or `CompleteAsync` is not called, the transaction will be rolled back. It means all the DB operations will be reverted back. Only after successfully executing `CompleteAsync` will the transaction be permanently committed to the database.
3. **CompleteAsync**
```csharp
using (var uow = _unitOfWorkManager.Begin())
{
// Execute database operations
await uow.CompleteAsync();
}
```
When you manually control the Unit of Work with `UnitOfWorkManager`, the `CompleteAsync` method is crucial for transaction completion. The unit of work maintains a `DbTransaction` object internally, and the `CompleteAsync` method invokes `DbTransaction.CommitAsync` to commit the transaction. The transaction will not be committed if `CompleteAsync` is either not executed or fails to execute successfully.
This method not only commits all database transactions but also:
- Executes and processes all pending domain events within the Unit of Work
- Executes all registered post-operations and cleanup tasks within the Unit of Work
- Releases all DbTransaction resources upon disposal of the Unit of Work object
> Note: `CompleteAsync` method should be called only once. Multiple calls are not supported.
#### In Non-Transactional Unit of Work
In non-transactional Unit of Work, these methods behave differently:
Both `autoSave` and `SaveChangesAsync` will persist changes to the database immediately, and these changes cannot be rolled back. Even in non-transactional Unit of Work, calling the `CompleteAsync` method remains necessary as it handles other essential tasks.
Example:
```csharp
using (var uow = _unitOfWorkManager.Begin(isTransactional: false))
{
// Changes are persisted immediately and cannot be rolled back
await _repository.InsertAsync(entity1, autoSave: true);
// This operation persists independently of the previous operation
await _repository.InsertAsync(entity2, autoSave: true);
await uow.CompleteAsync();
}
```
### Methods to Roll Back Transactions
#### In Transactional Unit of Work
A unit of work provides multiple approaches to roll back transactions:
1. **Automatic Rollback**
For transactions automatically managed by the ABP Framework, any uncaught exceptions during the request will trigger an automatic rollback.
2. **Manual Rollback**
For manually managed transactions, you can explicitly invoke the `RollbackAsync` method to immediately roll back the current transaction.
> Important: Once `RollbackAsync` is called, the entire Unit of Work transaction will be rolled back immediately, and any subsequent calls to `CompleteAsync` will have no effect.
```csharp
using (var uow = _unitOfWorkManager.Begin(
isTransactional: true,
isolationLevel: IsolationLevel.RepeatableRead,
timeout: 30
))
{
await _repository.InsertAsync(entity);
if (someCondition)
{
await uow.RollbackAsync();
return;
}
await uow.CompleteAsync();
}
```
The `CompleteAsync` method attempts to commit the transaction. If any exceptions occur during this process, the transaction will not be committed.
Here are two common exception scenarios:
1. **Exception Handling Within Unit of Work**
```csharp
using (var uow = _unitOfWorkManager.Begin(
isTransactional: true,
isolationLevel: IsolationLevel.RepeatableRead,
timeout: 30
))
{
try
{
await _bookRepository.InsertAsync(book);
await uow.SaveChangesAsync();
await _productRepository.UpdateAsync(product);
await uow.CompleteAsync();
}
catch (Exception)
{
// Exceptions can occur in InsertAsync, SaveChangesAsync, UpdateAsync, or CompleteAsync
// Even if some operations succeed, the transaction remains uncommitted to the database
// While you can explicitly call RollbackAsync to roll back the transaction,
// the transaction will not be committed anyway if CompleteAsync fails to execute
throw;
}
}
```
2. **Exception Handling Outside Unit of Work**
```csharp
try
{
using (var uow = _unitOfWorkManager.Begin(
isTransactional: true,
isolationLevel: IsolationLevel.RepeatableRead,
timeout: 30
))
{
await _bookRepository.InsertAsync(book);
await uow.SaveChangesAsync();
await _productRepository.UpdateAsync(product);
await uow.CompleteAsync();
}
}
catch (Exception)
{
// Exceptions can occur in UpdateAsync, SaveChangesAsync, UpdateAsync, or CompleteAsync
// Even if some operations succeed, the transaction remains uncommitted to the database
// Since CompleteAsync was not successfully executed, the transaction will not be committed
throw;
}
```
#### In Non-Transactional Unit of Work
In non-transactional units of work, operations are irreversible. Changes saved using `autoSave: true` or `SaveChangesAsync()` are persisted immediately, and the `RollbackAsync` method has no effect.
## Transaction Management Best Practices
### 1. Remember to Commit Transactions
When manually controlling transactions, remember to call the `CompleteAsync` method to commit the transaction after operations are complete.
### 2. Pay Attention to Context
If a unit of work already exists in the current context, `UnitOfWorkManager.Begin` method and` UnitOfWorkAttribute` will **reuse it**. Specify `requiresNew: true` to force create a new unit of work.
```csharp
[UnitOfWork]
public async Task Method1()
{
using (var uow = _unitOfWorkManager.Begin(
requiresNew: true,
isTransactional: true,
isolationLevel: IsolationLevel.RepeatableRead,
timeout: 30
))
{
await Method2();
await uow.CompleteAsync();
}
}
```
### 3. Use `virtual` Methods
To be able to use Unit of Work attribute, you must use the `virtual` modifier for methods in dependency injection class services, because ABP Framework uses interceptors, and it cannot intercept non `virtual` methods, thus unable to implement Unit of Work functionality.
### 4. Avoid Long Transactions
Enabling long-running transactions can lead to resource locking, excessive transaction log usage, and reduced concurrent performance, while rollback costs are high and may exhaust database connection resources. It's recommended to split into shorter transactions, reduce lock holding time, and optimize performance and reliability.
## Transaction-Related Recommendations
- Choose appropriate transaction isolation levels based on business requirements
- Avoid overly long transactions, long-running operations should be split into multiple small transactions
- Use the `requiresNew` parameter reasonably to control transaction boundaries
- Pay attention to setting appropriate transaction timeout periods
- Ensure transactions can properly roll back when exceptions occur
- For read-only operations, it's recommended to use non-transactional Unit of Work to improve performance
## References
- [ABP Unit of Work](https://abp.io/docs/latest/framework/architecture/domain-driven-design/unit-of-work)
- [EF Core Transactions](https://docs.microsoft.com/en-us/ef/core/saving/transactions)
- [Transaction Isolation Levels](https://docs.microsoft.com/en-us/dotnet/api/system.data.isolationlevel)

BIN
docs/en/Community-Articles/2025-01-24-Understanding-Transactions-in-ABP-Unit-Of-Work/pic.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

11
docs/en/cli/index.md

@ -883,18 +883,13 @@ abp translate -c zh-Hans --online --deepl-auth-key <auth-key>
### login
Some features of the CLI requires to be logged in to ABP Platform. To login with your username write:
Some features of the CLI requires to be logged in to ABP Platform. The login command supports the following usage options:
```bash
abp login <username> # Allows you to enter your password hidden
abp login <username> -p <password> # Specify the password as a parameter (password is visible)
abp login <username> --organization <organization> # If you have multiple organizations, you need set your active organization
abp login <username> -p <password> -o <organization> # You can enter both your password and organization in the same command
abp login <username> --device # Use device login flow
abp login # Opens a default browser to log in to ABP Platform via abp.io
abp login --device # Use device login flow
```
> When using the -p parameter, be careful as your password will be visible. It's useful for CI/CD automation pipelines.
A new login with an already active session overwrites the previous session.
### login-info

8
docs/en/docs-nav.json

@ -1430,6 +1430,10 @@
"text": "LeptonX Lite",
"path": "ui-themes/lepton-x-lite/blazor.md"
},
{
"text": "LeptonX",
"path": "ui-themes/lepton-x/blazor.md"
},
{
"text": "Branding",
"path": "framework/ui/blazor/branding.md"
@ -1751,6 +1755,10 @@
{
"text": "LeptonX Lite",
"path": "ui-themes/lepton-x-lite/angular.md"
},
{
"text": "LeptonX",
"path": "ui-themes/lepton-x/angular.md"
}
]
},

15
docs/en/framework/ui/blazor/overall.md

@ -1,14 +1,17 @@
# Blazor UI: Overall
## Introduction
[Blazor](https://docs.microsoft.com/en-us/aspnet/core/blazor/) is a framework for building interactive client-side web UI with .NET. It enables .NET developers to create Single-Page Web Applications using C# and the Razor syntax.
[Blazor](https://docs.microsoft.com/en-us/aspnet/core/blazor/) is a framework for building interactive client-side web UI with .NET. It is promising for a .NET developer that you can create Single-Page Web Applications using C# and the Razor syntax.
ABP provides comprehensive infrastructure and integrations that make your Blazor development easier, comfortable and enjoyable. ABP supports multiple Blazor hosting models:
ABP provides infrastructure and integrations that make your Blazor development even easier, comfortable and enjoyable.
* **Blazor WebAssembly (WASM)**: Client-side hosting model where the entire application runs in the browser using WebAssembly
* **Blazor Server**: Server-side hosting model with a real-time SignalR connection
* **Blazor WebApp**: The new hybrid/united model introduced in .NET 8 combining the benefits of Server and WebAssembly approaches
* **MAUI Blazor**: For building cross-platform native applications using Blazor & MAUI
This document provides an overview for the ABP Blazor UI integration and highlights some major features.
This document provides an overview of the ABP Blazor UI integration and highlights some major features.
### Getting Started
## Getting Started
You can follow the documents below to start with the ABP and the Blazor UI now:
@ -94,7 +97,7 @@ These libraries are selected as the base libraries and available to the applicat
> Bootstrap's JavaScript part is not used since the Blazorise library already provides the necessary functionalities to the Bootstrap components in a native way.
> Beginning from June, 2021, the Blazorise library has dual licenses; open source & commercial. Based on your yearly revenue, you may need to buy a commercial license. See [this post](https://blazorise.com/news/announcing-2022-blazorise-plans-and-pricing-updates) to learn more. The Blazorise license is bundled with ABP and commercial customers doesnt need to buy an extra Blazorise license.
> Beginning from June, 2021, the Blazorise library has dual licenses; open source & commercial. Based on your yearly revenue, you may need to buy a commercial license. See [this post](https://blazorise.com/news/announcing-2022-blazorise-plans-and-pricing-updates) to learn more. The Blazorise license is bundled with ABP and commercial customers doesn't need to buy an extra Blazorise license.
### The Layout

2
docs/en/framework/ui/blazor/theming.md

@ -27,7 +27,7 @@ Currently, three themes are **officially provided**:
* The [Basic Theme](basic-theme.md) is the minimalist theme with the plain Bootstrap style. It is **open source and free**.
* The [Lepton Theme](https://abp.io/themes) is a **commercial** theme developed by the core ABP team and is a part of the [ABP](https://abp.io/) license.
* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has a [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/blazor) and a [lite](../../../ui-themes/lepton-x-lite/blazor.md) version.
* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has a [commercial](../../../ui-themes/lepton-x/blazor.md) and a [lite](../../../ui-themes/lepton-x-lite/blazor.md) version.
## Overall

8
docs/en/framework/ui/mvc-razor-pages/tag-helpers/form-elements.md

@ -10,7 +10,7 @@ See the [form elements demo page](https://bootstrap-taghelpers.abp.io/Components
## abp-input
`abp-input` tag creates a Bootstrap form input for a given c# property. It uses [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-input-tag-helper) in background, so every data annotation attribute of `input` tag helper of Asp.Net Core is also valid for `abp-input`.
`abp-input` tag creates a Bootstrap form input for a given c# property. It uses [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-9.0#the-input-tag-helper) in background, so every data annotation attribute of `input` tag helper of Asp.Net Core is also valid for `abp-input`.
Usage:
@ -89,7 +89,7 @@ You can set some of the attributes on your c# property, or directly on HTML tag.
* `required-symbol`: Adds the required symbol `(*)` to the label when the input is required. The default value is `True`.
* `floating-label`: Sets the label as floating label. The default value is `False`.
`asp-format`, `name` and `value` attributes of [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-input-tag-helper) are also valid for `abp-input` tag helper.
`asp-format`, `name` and `value` attributes of [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-9.0#the-input-tag-helper) are also valid for `abp-input` tag helper.
### Label & Localization
@ -101,7 +101,7 @@ You can set the label of the input in several ways:
## abp-select
`abp-select` tag creates a Bootstrap form select for a given c# property. It uses [ASP.NET Core Select Tag Helper](https://docs.microsoft.com/tr-tr/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-select-tag-helper) in background, so every data annotation attribute of `select` tag helper of ASP.NET Core is also valid for `abp-select`.
`abp-select` tag creates a Bootstrap form select for a given c# property. It uses [ASP.NET Core Select Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-9.0#the-select-tag-helper) in background, so every data annotation attribute of `select` tag helper of ASP.NET Core is also valid for `abp-select`.
`abp-select` tag needs a list of `Microsoft.AspNetCore.Mvc.Rendering.SelectListItem ` to work. It can be provided by `asp-items` attriube on the tag or `[SelectItems()]` attribute on c# property. (if you are using [abp-dynamic-form](dynamic-forms.md), c# attribute is the only way.)
@ -432,4 +432,4 @@ newPicker.insertAfter($('body'));
* `startDateName`: Sets the name of the hidden start date input.
* `endDateName`: Sets the name of the hidden end date input.
* `dateName`: Sets the name of the hidden date input.
* Other [datepicker options](https://www.daterangepicker.com/#options). Eg: `startDate: "2020-01-01"`.
* Other [datepicker options](https://www.daterangepicker.com/#options). Eg: `startDate: "2020-01-01"`.

2
docs/en/modules/account/idle-session-timeout.md

@ -6,7 +6,7 @@ The `Idle Session Timeout` feature allows you to automatically log out users aft
You can enable/disable the `Idle Session Timeout` feature in the `Setting > Account > Idle Session Timeout` page.
The default idle session timeout is 1 hour. You can change it by selecting a different value from the dropdown list or entering a custom value(in minutes).
The default idle session timeout is 1 hour. You can change it by selecting a different value from the dropdown list or entering a custom value (in minutes).
![idle-setting](../../images/idle-setting.png)

106
docs/en/samples/index.md

@ -1,6 +1,6 @@
# ABP Samples
Here, a list of official samples built with ABP.
This document provides a list of samples built with ABP. Each sample is briefly explained below, along with its live demo (if available), source code, and tutorial links (where applicable).
## Event Hub
@ -11,13 +11,13 @@ A reference application built with ABP. It implements the Domain Driven Design w
## eShopOnAbp
> ⚠️ **Important Notice**
> This project, "eShopOnAbp," is outdated. It served as a reference project for microservice architecture using the ABP Framework, but we now recommend using the [ABP Microservice Solution Template](https://abp.io/docs/latest/solution-templates/microservice) for new projects.
Reference microservice solution built with ABP and .NET.
* [Source code](https://github.com/abpframework/eShopOnAbp)
> ⚠️ **Important Notice**
> This project, "eShopOnAbp," is outdated. It served as a reference project for microservice architecture using the ABP Framework, but we now recommend using the [ABP Microservice Solution Template](https://abp.io/docs/latest/solution-templates/microservice) for new projects.
## CMS Kit Demo
A minimal example website built with the [CMS Kit module](../modules/cms-kit/index.md).
@ -34,92 +34,48 @@ A middle-size CRM application built with ABP.
## Book Store
A simple CRUD application to show basic principles of developing an application with ABP. The same sample was implemented with different technologies and different modules.
### With Open Source Modules
The following samples uses only the open source (free) modules.
A simple CRUD application to show basic principles of developing an application with ABP. The same sample was implemented with different technologies and different modules:
* **Book Store: Razor Pages UI & Entity Framework Core**
* [Tutorial](../tutorials/book-store/part-01.md?UI=MVC&DB=EF)
* [Source code](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Download source code (with PRO modules) *](https://abp.io/Account/Login?returnUrl=/api/download/samples/bookstore-mvc-ef)
* **Book Store: Blazor UI & Entity Framework Core**
* [Tutorial](../tutorials/book-store/part-01.md?UI=Blazor&DB=EF)
* [Source code](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Download source code (with PRO modules) *](https://abp.io/Account/Login?returnUrl=/api/download/samples/bookstore-blazor-efcore)
* **Book Store: Angular UI & MongoDB**
* [Tutorial](../tutorials/book-store/part-01.md?UI=NG&DB=Mongo)
* [Source code](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
* [Download source code (with PRO modules) *](https://abp.io/Account/Login?returnUrl=/api/download/samples/bookstore-angular-mongodb)
* **Book Store: Modular application (Razor Pages UI & EF Core)**
* [Source code](https://github.com/abpframework/abp-samples/tree/master/BookStore-Modular)
### With Pro Modules
If you want to create the BookStore application and generate CRUD pages automatically with ABP Suite, please refer to the [Book Store Application (with ABP Suite) tutorial](../tutorials/book-store-with-abp-suite/part-01.md). Also, you can follow the [Mobile Application Development Tutorials](../tutorials/mobile/index.md), if you want to implement the CRUD operations for [MAUI](../tutorials/mobile/maui/index.md) & [React Native](../tutorials/mobile/react-native/index.md) mobile applications.
The following samples uses the pro modules.
> **Note:** _Downloading source codes (with PRO modules) \*_ require an active [ABP License](https://abp.io/pricing).
- **Book Store: Razor Pages (MVC) UI & Entity Framework Core**
- [Tutorial](../tutorials/book-store/part-01.md?UI=MVC&DB=EF)
- [Download the source code](https://abp.io/Account/Login?returnUrl=/api/download/samples/bookstore-mvc-ef)
- **Book Store: Blazor UI & Entity Framework Core**
- [Tutorial](../tutorials/book-store/part-01.md?UI=Blazor&DB=EF)
- [Download the source code](https://abp.io/Account/Login?returnUrl=/api/download/samples/bookstore-blazor-efcore)
- **Book Store: Angular UI & MongoDB**
- [Tutorial](../tutorials/book-store/part-01.md?UI=NG&DB=Mongo)
- [Download the source code](https://abp.io/Account/Login?returnUrl=/api/download/samples/bookstore-angular-mongodb)
## ModularCRM
A modular monolith application that demonstrates how to create, compose, and communicate between application modules to build a modular web application:
* **ModularCRM: Razor Pages UI & Entity Framework Core**
* [Tutorial](../tutorials/modular-crm/part-01.md?UI=MVC&DB=EF)
* [Source code](https://github.com/abpframework/abp-samples/tree/master/ModularCrm)
## CloudCrm
> This tutorial & sample application is suitable for those who have an [ABP Business or a higher license](https://abp.io/pricing).
A microservice solution that shows how to start a new microservice solution, create services and communicate between these services. It's a reference tutorial to learn to use these services from a web application through an API gateway and automatically generate CRUD pages using the ABP Suite tool:
* **CloudCRM: Razor Pages UI & Entity Framework Core**
* [Tutorial](../tutorials/microservice/part-01.md?UI=MVC&DB=EF)
* [Download source code](https://abp.io/api/download/samples/cloud-crm-mvc-ef)
## Other Samples
* **Event Organizer**: A sample application to create events (meetups) and allow others to register the events. Developed using EF Core and Blazor UI.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/EventOrganizer)
* [Article](https://abp.io/community/articles/creating-an-event-organizer-application-with-the-blazor-ui-wbe0sf2z)
* **Entity Framework Migrations**: A solution to demonstrate how to split your application into multiple databases each database contains different modules.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/EfCoreMigrationDemo)
* [EF Core database migrations document](../framework/data/entity-framework-core/migrations.md)
* **SignalR Demo**: A simple chat application that allows to send and receive messages among authenticated users.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo)
* [SignalR Integration document](../framework/real-time/signalr.md)
* **Real Time Messaging In A Distributed Architecture** (using SingalR & RabbitMQ)
* [Source code](https://github.com/abpframework/abp-samples/tree/master/SignalRTieredDemo)
* [Article](https://abp.io/community/articles/real-time-messaging-in-a-distributed-architecture-using-abp-framework-singalr-rabbitmq-daf47e17)
* **Dashboard Demo**: A simple application to show how to use the widget system for the ASP.NET Core MVC UI.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/DashboardDemo)
* [Widget documentation](../framework/ui/mvc-razor-pages/widgets.md)
* **RabbitMQ Event Bus Demo**: A solution consists of two applications communicating to each other via distributed events with RabbitMQ integration.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/RabbitMqEventBus)
* [Distributed event bus document](../framework/infrastructure/event-bus/distributed)
* [RabbitMQ distributed event bus integration document](../framework/infrastructure/event-bus/distributed/rabbitmq.md)
* **Text Templates Demo**: Shows different use cases of the text templating system.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo)
* [Text templating documentation](../framework/infrastructure/text-templating)
* **Stored Procedure Demo**: Demonstrates how to use stored procedures, database views and functions with best practices.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/StoredProcedureDemo)
* **Passwordless Authentication**: Shows how to add a custom token provider to authenticate a user with a link, instead of entering a password.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/PasswordlessAuthentication)
* [Article](https://abp.io/community/articles/implementing-passwordless-authentication-with-asp.net-core-identity-c25l8koj)
* **Authentication Customization**: A solution to show how to customize the authentication for ASP.NET Core MVC / Razor Pages applications.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/Authentication-Customization)
* Related articles:
* [Azure Active Directory Authentication](https://abp.io/community/articles/how-to-use-the-azure-active-directory-authentication-for-mvc-razor-page-applications-4603b9cf)
* [Customize the Login Page](https://abp.io/community/articles/how-to-customize-the-login-page-for-mvc-razor-page-applications-9a40f3cd)
* [Customize the SignIn Manager](https://abp.io/community/articles/how-to-customize-the-signin-manager-3e858753)
* **GRPC Demo**: Shows how to add a gRPC service to an ABP based web application and consume it from a console application.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo)
* **Telerik Blazor Integration**: Shows how to install and use Telerik Blazor components with ABP.
* [Article](https://abp.io/community/articles/how-to-integrate-the-telerik-blazor-components-to-the-abp-blazor-ui-q8g31abb)
* **Angular Material Integration**: Implemented the web application tutorial using the Angular Material library.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/AcmeBookStoreAngularMaterial)
* [Article](https://abp.io/community/articles/using-angular-material-components-with-the-abp-framework-af8ft6t9)
* **DevExtreme Angular Component Integration**: How to install and use DevExtreme components in the ABP Angular UI.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/DevExtreme-Angular)
* [Article](https://abp.io/community/articles/using-devextreme-angular-components-with-the-abp-framework-x5nyvj3i)
* **DevExtreme MVC / Razor Pages Component Integration**: How to install and use DevExtreme components in the ABP MVC / Razor Pages UI.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/DevExtreme-Mvc)
* [Article](https://abp.io/community/articles/using-devextreme-components-with-the-abp-framework-zb8z7yqv)
* **Syncfusion Blazor Integration**: Shows how to install and integrate Syncfusion UI the ABP Blazor UI.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/SyncfusionSample)
* [Article](https://abp.io/community/articles/using-syncfusion-components-with-the-abp-framework-5ccvi8kc)
* **Empty ASP.NET Core Application**: The most basic ASP.NET Core application with ABP installed.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/BasicAspNetCoreApplication)
* [Documentation](../get-started/empty-aspnet-core-application.md)
* **Using Elsa Workflow with ABP**: Shows how to use the Elsa Core workflow library within an ABP-based application.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/ElsaDemo)
* [Article](https://abp.io/community/articles/using-elsa-workflow-with-the-abp-framework-773siqi9)
ABP Platform provides many sample applications demonstrating various use cases and integrations. You can:
* Browse all sample applications in the [abp-samples repository](https://github.com/abpframework/abp-samples).
* Read detailed articles and tutorials in the [ABP Community](https://abp.io/community), which are shared by ABP Community & Contributors.

2
docs/en/solution-templates/layered-web-application/deployment/azure-deployment/step3-deployment-github-action.md

@ -295,7 +295,7 @@ jobs:
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp-3.outputs.webapp-url }}
steps:
- name: Download artifact from apihost
uses: actions/download-artifact@v4
with:

217
docs/en/solution-templates/layered-web-application/deployment/deployment-docker-compose.md

@ -15,7 +15,7 @@ This guide will guide you through how to build docker images for your applicatio
## Building Docker Images
Each application contains a dockerfile called `Dockerfile.local` for building the docker image. As the naming implies, these Dockerfiles are not multi-stage Dockerfiles and require the project to be built in `Release` mode to create the image. Currently, if you are building your images using CI & CD pipeline, you either need to include the SDK to your pipeline before building the images or add your own [multi-stage dockerfiles](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/docker/building-net-docker-images?view=aspnetcore-7.0).
Each application contains a dockerfile called `Dockerfile.local` for building the docker image. As the naming implies, these Dockerfiles are not multi-stage Dockerfiles and require the project to be built in `Release` mode to create the image. Currently, if you are building your images using CI & CD pipeline, you either need to include the SDK to your pipeline before building the images or add your own [multi-stage dockerfiles](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/docker/building-net-docker-images?view=aspnetcore-9.0).
Since they are not multi-staged Dockerfiles, if you want to build the images individually, you can navigate to the related to-be-hosted application folder and run the following command:
@ -31,7 +31,7 @@ docker build -f Dockerfile.local -t mycompanyname/myappname:version .
To manually build your application image.
To ease the process, application templates provide a build script to build all the images with a single script under `etc/build` folder named `build-images-locally.ps1`.
To ease the process, application templates provide a build script to build all the images with a single script under `etc/docker-compose` folder named `build-images-locally.ps1`.
Based on your application name, UI and type, a build image script will be generated.
{{ if UI == "MVC"}}
@ -204,8 +204,8 @@ DbMigrator is a console application that is used to migrate the database of your
`Dockerfile.local` is provided under this project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:7.0
COPY bin/Release/net7.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:9.0
COPY bin/Release/net9.0/publish/ app/
WORKDIR /app
ENTRYPOINT ["dotnet", "BookStore.DbMigrator.dll"]
```
@ -226,7 +226,7 @@ docker build -f Dockerfile.local -t acme/bookstore-db-migrator:latest . #Builds
In the **WebModule** under authentication configuration, there is an extra configuration for containerized environment support:
```csharp
if (Convert.ToBoolean(configuration["AuthServer:IsContainerizedOnLocalhost"]))
if (Convert.ToBoolean(configuration["AuthServer:IsOnK8s"]))
{
context.Services.Configure<OpenIdConnectOptions>("oidc", options =>
{
@ -268,13 +268,13 @@ if (Convert.ToBoolean(configuration["AuthServer:IsContainerizedOnLocalhost"]))
This is used when the **AuthServer is running on docker containers(or pods)** to configure the redirection URLs for the internal network and the web. The application must be redirected to real DNS (localhost in this case) when the `/authorize` and `/logout` requests over the browser but handle the token validation inside the isolated network without going out to the internet. `"AuthServer:MetaAddress"` appsetting should indicate the container/pod service name while the `AuthServer:Authority` should be pointing to real DNS for the browser to redirect.
The `appsettings.json` file does not contain `AuthServer:IsContainerizedOnLocalhost` and `AuthServer:MetaAddress` settings since they are used for orchestrated deployment scenarios, you can see these settings are overridden by the `docker-compose.yml` file.
The `appsettings.json` file does not contain `AuthServer:IsOnK8s` and `AuthServer:MetaAddress` settings since they are used for orchestrated deployment scenarios, you can see these settings are overridden by the `docker-compose.yml` file.
`Dockerfile.local` is provided under this project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:7.0
COPY bin/Release/net7.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:9.0
COPY bin/Release/net9.0/publish/ app/
WORKDIR /app
ENTRYPOINT ["dotnet", "Acme.BookStore.Web.dll"]
```
@ -289,11 +289,11 @@ docker build -f Dockerfile.local -t acme/bookstore-web:latest . #Builds the imag
​ {{ end }} {{ if Tiered == "No" }}MVC/Razor Pages application is a server-side rendering application that contains both the OpenID-provider and the Http.Api endpoints within self; it will be a single application to deploy. `Dockerfile.local` is provided under this project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
COPY bin/Release/net7.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
COPY bin/Release/net9.0/publish/ app/
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
RUN dotnet dev-certs https -v -ep authserver.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED
@ -318,36 +318,17 @@ if (!hostingEnvironment.IsDevelopment())
options.AddDevelopmentEncryptionAndSigningCertificate = false;
});
PreConfigure<OpenIddictServerBuilder>(builder =>
PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
{
builder.AddSigningCertificate(GetSigningCertificate(hostingEnvironment, configuration));
builder.AddEncryptionCertificate(GetSigningCertificate(hostingEnvironment, configuration));
builder.SetIssuer(new Uri(configuration["AuthServer:Authority"]));
serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", configuration["AuthServer:CertificatePassPhrase"]!);
serverBuilder.SetIssuer(new Uri(configuration["AuthServer:Authority"]!));
});
}
```
This configuration disables the *DevelopmentEncryptionAndSigningCertificate* and uses a self-signed certificate called `authserver.pfx`. for **signing and encrypting the tokens**. This certificate is created when the docker image is built using the `dotnet dev-certs` tooling. It is a sample-generated certificate, and it is **recommended** to update it for the production environment. You can check the [OpenIddict Encryption and signing credentials documentation](https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html) for different options and customization.
The `GetSigningCertificate` method is a private method located under the same **WebModule**:
```csharp
private X509Certificate2 GetSigningCertificate(IWebHostEnvironment hostingEnv, IConfiguration configuration)
{
var fileName = "authserver.pfx";
var passPhrase = "2D7AA457-5D33-48D6-936F-C48E5EF468ED";
var file = Path.Combine(hostingEnv.ContentRootPath, fileName);
if (!File.Exists(file))
{
throw new FileNotFoundException($"Signing Certificate couldn't found: {file}");
}
return new X509Certificate2(file, passPhrase);
}
```
> You can always create any self-signed certificate using any other tooling outside the Dockerfile. You need to remember to set them as **embedded resource** since the `GetSigningCertificate` method will be checking this file physically.
> You can always create any self-signed certificate using any other tooling outside the Dockerfile. You need to remember to set them as **embedded resource**.
If you don't want to use the `build-images-locally.ps1` to build the images or to build this image individually and manually, navigate to the **Web** folder and run:
@ -369,7 +350,7 @@ docker build -f Dockerfile.local -t acme/bookstore-web:latest . #Builds the imag
In the **BlazorModule** under authentication configuration, there is an extra configuration for containerized environment support:
```csharp
if (Convert.ToBoolean(configuration["AuthServer:IsContainerizedOnLocalhost"]))
if (Convert.ToBoolean(configuration["AuthServer:IsOnK8s"]))
{
context.Services.Configure<OpenIdConnectOptions>("oidc", options =>
{
@ -411,13 +392,13 @@ if (Convert.ToBoolean(configuration["AuthServer:IsContainerizedOnLocalhost"]))
This is used when the **AuthServer is running on docker containers(or pods)** to configure the redirection URLs for the internal network and the web. The application must be redirected to real DNS (localhost in this case) when the `/authorize` and `/logout` requests over the browser but handle the token validation inside the isolated network without going out to the internet. `"AuthServer:MetaAddress"` appsetting should indicate the container/pod service name while the `AuthServer:Authority` should be pointing to real DNS for the browser to redirect.
The `appsettings.json` file does not contain `AuthServer:IsContainerizedOnLocalhost` and `AuthServer:MetaAddress` settings since they are used for orchestrated deployment scenarios, you can see these settings are overridden by the `docker-compose.yml` file.
The `appsettings.json` file does not contain `AuthServer:IsOnK8s` and `AuthServer:MetaAddress` settings since they are used for orchestrated deployment scenarios, you can see these settings are overridden by the `docker-compose.yml` file.
`Dockerfile.local` is provided under this project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:7.0
COPY bin/Release/net7.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:9.0
COPY bin/Release/net9.0/publish/ app/
WORKDIR /app
ENTRYPOINT ["dotnet", "Acme.BookStore.Blazor.dll"]
```
@ -432,11 +413,11 @@ docker build -f Dockerfile.local -t acme/bookstore-blazor:latest . #Builds the i
​ {{ end }} {{ if Tiered == "No" }}Blazor Server application is a server-side rendering application that contains both the OpenID-provider and the Http.Api endpoints within self; it will be a single application to deploy. `Dockerfile.local` is provided under this project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
COPY bin/Release/net7.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
COPY bin/Release/net9.0/publish/ app/
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
RUN dotnet dev-certs https -v -ep authserver.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED
@ -461,36 +442,17 @@ if (!hostingEnvironment.IsDevelopment())
options.AddDevelopmentEncryptionAndSigningCertificate = false;
});
PreConfigure<OpenIddictServerBuilder>(builder =>
PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
{
builder.AddSigningCertificate(GetSigningCertificate(hostingEnvironment, configuration));
builder.AddEncryptionCertificate(GetSigningCertificate(hostingEnvironment, configuration));
builder.SetIssuer(new Uri(configuration["AuthServer:Authority"]));
serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", configuration["AuthServer:CertificatePassPhrase"]!);
serverBuilder.SetIssuer(new Uri(configuration["AuthServer:Authority"]!));
});
}
```
This configuration disables the *DevelopmentEncryptionAndSigningCertificate* and uses a self-signed certificate called `authserver.pfx`. for **signing and encrypting the tokens**. This certificate is created when the docker image is built using the `dotnet dev-certs` tooling. It is a sample-generated certificate, and it is **recommended** to update it for the production environment. You can check the [OpenIddict Encryption and signing credentials documentation](https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html) for different options and customization.
The `GetSigningCertificate` method is a private method located under the same **BlazorModule**:
```csharp
private X509Certificate2 GetSigningCertificate(IWebHostEnvironment hostingEnv, IConfiguration configuration)
{
var fileName = "authserver.pfx";
var passPhrase = "2D7AA457-5D33-48D6-936F-C48E5EF468ED";
var file = Path.Combine(hostingEnv.ContentRootPath, fileName);
if (!File.Exists(file))
{
throw new FileNotFoundException($"Signing Certificate couldn't found: {file}");
}
return new X509Certificate2(file, passPhrase);
}
```
> You can always create any self-signed certificate using any other tooling outside the dockerfile. You need to remember to set them as **embedded resource** since the `GetSigningCertificate` method will be checking this file physically.
> You can always create any self-signed certificate using any other tooling outside the Dockerfile. You need to remember to set them as **embedded resource**.
If you don't want to use the `build-images-locally.ps1` to build the images or to build this image individually and manually, navigate to the **BlazorModule** folder and run:
@ -562,7 +524,7 @@ server {
}
```
This configuration allows returning the `dynamic-env.json` file as a static file, which ABP Angular application uses for environment variables in one of the first initial requests when rendering the page. **The `dynamic-env.json` file you need to override is located under `aspnet-core/etc/docker`** folder.
This configuration allows returning the `dynamic-env.json` file as a static file, which ABP Angular application uses for environment variables in one of the first initial requests when rendering the page. **The `dynamic-env.json` file you need to override is located under `aspnet-core/etc/docker-compose`** folder.
​ {{ if Tiered == "No" }}
@ -645,8 +607,8 @@ docker build -f Dockerfile.local -t acme/bookstore-angular:latest . #Builds the
The Blazor application uses [nginx:alpine-slim](https://hub.docker.com/layers/library/nginx/alpine-slim/images/sha256-0f859db466fda2c52f62b48d0602fb26867d98edbd62c26ae21414b3dea8d8f4?context=explore) base image to host the blazor application. You can modify the base image based on your preference in the `Dockerfile.local` which provided under the Blazor folder of your solution as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS build
COPY bin/Release/net7.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS build
COPY bin/Release/net9.0/publish/ app/
FROM nginx:alpine-slim AS final
WORKDIR /usr/share/nginx/html
@ -701,11 +663,11 @@ docker build -f Dockerfile.local -t acme/bookstore-blazor:latest . #Builds the i
This is the backend application that contains the openid-provider functionality as well. The `dockerfile.local` is located under the `Http.Api.Host` project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
COPY bin/Release/net7.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
COPY bin/Release/net9.0/publish/ app/
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
RUN dotnet dev-certs https -v -ep authserver.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED
@ -730,36 +692,17 @@ if (!hostingEnvironment.IsDevelopment())
options.AddDevelopmentEncryptionAndSigningCertificate = false;
});
PreConfigure<OpenIddictServerBuilder>(builder =>
PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
{
builder.AddSigningCertificate(GetSigningCertificate(hostingEnvironment, configuration));
builder.AddEncryptionCertificate(GetSigningCertificate(hostingEnvironment, configuration));
builder.SetIssuer(new Uri(configuration["AuthServer:Authority"]));
serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", configuration["AuthServer:CertificatePassPhrase"]!);
serverBuilder.SetIssuer(new Uri(configuration["AuthServer:Authority"]!));
});
}
```
This configuration disables the *DevelopmentEncryptionAndSigningCertificate* and uses a self-signed certificate called `authserver.pfx`. for **signing and encrypting the tokens**. This certificate is created when the docker image is built using the `dotnet dev-certs` tooling. It is a sample-generated certificate, and it is **recommended** to update it for the production environment. You can check the [OpenIddict Encryption and signing credentials documentation](https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html) for different options and customization.
The `GetSigningCertificate` method is a private method located under the same **HttpApiHostModule**:
```csharp
private X509Certificate2 GetSigningCertificate(IWebHostEnvironment hostingEnv, IConfiguration configuration)
{
var fileName = "authserver.pfx";
var passPhrase = "2D7AA457-5D33-48D6-936F-C48E5EF468ED";
var file = Path.Combine(hostingEnv.ContentRootPath, fileName);
if (!File.Exists(file))
{
throw new FileNotFoundException($"Signing Certificate couldn't found: {file}");
}
return new X509Certificate2(file, passPhrase);
}
```
> You can always create any self-signed certificate using any other tooling outside of the dockerfile. You need to keep in mind to set them as **embedded resource** since the `GetSigningCertificate` method will be checking this file physically.
> You can always create any self-signed certificate using any other tooling outside the Dockerfile. You need to remember to set them as **embedded resource**.
If you don't want to use the `build-images-locally.ps1` to build the images or to build this image individually and manually, navigate to **Http.Api.Host** folder and run:
@ -777,11 +720,11 @@ docker build -f Dockerfile.local -t acme/bookstore-api:latest . #Builds the imag
This is the backend application that contains the OpenID-provider functionality as well. The `dockerfile.local` is located under the `Http.Api.Host` project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
COPY bin/Release/net7.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
COPY bin/Release/net9.0/publish/ app/
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
RUN dotnet dev-certs https -v -ep authserver.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED
@ -806,36 +749,17 @@ if (!hostingEnvironment.IsDevelopment())
options.AddDevelopmentEncryptionAndSigningCertificate = false;
});
PreConfigure<OpenIddictServerBuilder>(builder =>
PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
{
builder.AddSigningCertificate(GetSigningCertificate(hostingEnvironment, configuration));
builder.AddEncryptionCertificate(GetSigningCertificate(hostingEnvironment, configuration));
builder.SetIssuer(new Uri(configuration["AuthServer:Authority"]));
serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", configuration["AuthServer:CertificatePassPhrase"]!);
serverBuilder.SetIssuer(new Uri(configuration["AuthServer:Authority"]!));
});
}
```
This configuration disables the *DevelopmentEncryptionAndSigningCertificate* and uses a self-signed certificate called `authserver.pfx`. for **signing and encrypting the tokens**. This certificate is created when the docker image is built using the `dotnet dev-certs` tooling. It is a sample-generated certificate, and it is **recommended** to update it for the production environment. You can check the [OpenIddict Encryption and signing credentials documentation](https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html) for different customization options.
The `GetSigningCertificate` method is a private method located under the same **HttpApiHostModule**:
```csharp
private X509Certificate2 GetSigningCertificate(IWebHostEnvironment hostingEnv, IConfiguration configuration)
{
var fileName = "authserver.pfx";
var passPhrase = "2D7AA457-5D33-48D6-936F-C48E5EF468ED";
var file = Path.Combine(hostingEnv.ContentRootPath, fileName);
if (!File.Exists(file))
{
throw new FileNotFoundException($"Signing Certificate couldn't found: {file}");
}
return new X509Certificate2(file, passPhrase);
}
```
This configuration disables the *DevelopmentEncryptionAndSigningCertificate* and uses a self-signed certificate called `authserver.pfx`. for **signing and encrypting the tokens**. This certificate is created when the docker image is built using the `dotnet dev-certs` tooling. It is a sample-generated certificate, and it is **recommended** to update it for the production environment. You can check the [OpenIddict Encryption and signing credentials documentation](https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html) for different options and customization.
> You can always create any self-signed certificate using any other tooling outside the dockerfile. You need to remember to set them as **embedded resource** since the `GetSigningCertificate` method will be checking this file physically.
> You can always create any self-signed certificate using any other tooling outside the Dockerfile. You need to remember to set them as **embedded resource**.
If you don't want to use the `build-images-locally.ps1` to build the images or to build this image individually and manually, navigate to **Http.Api.Host** folder and run:
@ -855,11 +779,11 @@ docker build -f Dockerfile.local -t acme/bookstore-api:latest . #Builds the imag
This is the openid-provider application, the authentication server, which should be individually hosted compared to non-tiered application templates. The `dockerfile.local` is located under the `AuthServer` project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
COPY bin/Release/net7.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
COPY bin/Release/net9.0/publish/ app/
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
RUN dotnet dev-certs https -v -ep authserver.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED
@ -884,36 +808,17 @@ if (!hostingEnvironment.IsDevelopment())
options.AddDevelopmentEncryptionAndSigningCertificate = false;
});
PreConfigure<OpenIddictServerBuilder>(builder =>
PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
{
builder.AddSigningCertificate(GetSigningCertificate(hostingEnvironment, configuration));
builder.AddEncryptionCertificate(GetSigningCertificate(hostingEnvironment, configuration));
builder.SetIssuer(new Uri(configuration["AuthServer:Authority"]));
serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", configuration["AuthServer:CertificatePassPhrase"]!);
serverBuilder.SetIssuer(new Uri(configuration["AuthServer:Authority"]!));
});
}
```
This configuration disables the *DevelopmentEncryptionAndSigningCertificate* and uses a self-signed certificate called `authserver.pfx`. for **signing and encrypting the tokens**. This certificate is created when the docker image is built using the `dotnet dev-certs` tooling. It is a sample-generated certificate, and it is **recommended** to update it for the production environment. You can check the [OpenIddict Encryption and signing credentials documentation](https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html) for different options and customization.
The `GetSigningCertificate` method is a private method located under the same **AuthServerModule**:
```csharp
private X509Certificate2 GetSigningCertificate(IWebHostEnvironment hostingEnv, IConfiguration configuration)
{
var fileName = "authserver.pfx";
var passPhrase = "2D7AA457-5D33-48D6-936F-C48E5EF468ED";
var file = Path.Combine(hostingEnv.ContentRootPath, fileName);
if (!File.Exists(file))
{
throw new FileNotFoundException($"Signing Certificate couldn't found: {file}");
}
return new X509Certificate2(file, passPhrase);
}
```
> You can always create any self-signed certificate using any other tooling outside the dockerfile. You need to remember to set them as **embedded resource** since the `GetSigningCertificate` method will be checking this file physically.
> You can always create any self-signed certificate using any other tooling outside the Dockerfile. You need to remember to set them as **embedded resource**.
If you don't want to use the `build-images-locally.ps1` to build the images or to build this image individually and manually, navigate to the **AuthServer** folder and run:
@ -927,8 +832,8 @@ docker build -f Dockerfile.local -t acme/bookstore-authserver:latest . #Builds t
This is the backend application that exposes the endpoints and swagger UI. It is not a multi-stage dockerfile; hence you need to have already built this application in **Release mode** to use this dockerfile. The `dockerfile.local` is located under the `Http.Api.Host` project as below;
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:7.0
COPY bin/Release/net7.0/publish/ app/
FROM mcr.microsoft.com/dotnet/aspnet:9.0
COPY bin/Release/net9.0/publish/ app/
WORKDIR /app
ENTRYPOINT ["dotnet", "Acme.BookStore.HttpApi.Host.dll"]
```
@ -944,7 +849,7 @@ docker build -f Dockerfile.local -t acme/bookstore-api:latest . #Builds the imag
## Running Docker-Compose on Localhost
Under the `etc/docker` folder, you can find the `docker-compose.yml` to run your application. To ease the running process, the template provides `run-docker.ps1` (and `run-docker.sh`) scripts that handle the HTTPS certificate creation, which is used in environment variables;
Under the `etc/docker-compose` folder, you can find the `docker-compose.yml` to run your application. To ease the running process, the template provides `run-docker.ps1` (and `run-docker.sh`) scripts that handle the HTTPS certificate creation, which is used in environment variables;
```powershell
$currentFolder = $PSScriptRoot
@ -1203,7 +1108,7 @@ This is the angular application we deploy on http://localhost:4200 by default us
> Don't forget to rebuild the `acme/bookstore-angular:latest` image after updating the `nginx.conf` file.
The bookstore-angular service mounts the `etc/docker/dynamic-env.json` file to change the existing dynamic-env.json file, which is copied during image creation, to change the environment variables on deployment time instead of re-creating the docker image after each environmental variable change. **Do not forget to override the `dynamic-env.json` located under the `aspnet-core/etc/docker`** folder.
The bookstore-angular service mounts the `etc/docker-compose/dynamic-env.json` file to change the existing dynamic-env.json file, which is copied during image creation, to change the environment variables on deployment time instead of re-creating the docker image after each environmental variable change. **Do not forget to override the `dynamic-env.json` located under the `aspnet-core/etc/docker-compose`** folder.
> If you are not using Docker with WSL, you may have problems with the volume mount permissions. You need to grant docker to be able to use the local file system. See this [SO answer](https://stackoverflow.com/a/20652410) for more information.
@ -1438,7 +1343,7 @@ bookstore-web:
- Kestrel__Certificates__Default__Password=91f91912-5ab0-49df-8166-23377efaf3cc
- App__SelfUrl=https://localhost:44353
- AuthServer__RequireHttpsMetadata=false {{ if Tiered == "Yes" }}
- AuthServer__IsContainerizedOnLocalhost=true
- AuthServer__IsOnK8s=true
- AuthServer__Authority=https://localhost:44334/
- RemoteServices__Default__BaseUrl=http://bookstore-api
- RemoteServices__AbpAccountPublic__BaseUrl=http://bookstore-authserver
@ -1464,7 +1369,7 @@ This is the MVC/Razor Page application docker service is using the `acme/booksto
The MVC/Razor Page is a server-side rendering application that uses the **hybrid flow**. This flow uses **browser** to login/logout process to the OpenID-provider but issues the **access_token from the back-channel** (server-side). To achieve this functionality, the module class has extra `OpenIdConnectOptions` to override some of the events:
```csharp
if (Convert.ToBoolean(configuration["AuthServer:IsContainerizedOnLocalhost"]))
if (Convert.ToBoolean(configuration["AuthServer:IsOnK8s"]))
{
context.Services.Configure<OpenIdConnectOptions>("oidc", options =>
{
@ -1511,7 +1416,7 @@ if (Convert.ToBoolean(configuration["AuthServer:IsContainerizedOnLocalhost"]))
{{ if Tiered == "Yes" }}
- `AuthServer__IsContainerizedOnLocalhost` is the configuration to enable the **OpenIdConnectOptions** to provide a different endpoint for the MetaAddress of the OpenID-provider and intercepting the URLS for *authorization* and *logout* endpoints.
- `AuthServer__IsOnK8s` is the configuration to enable the **OpenIdConnectOptions** to provide a different endpoint for the MetaAddress of the OpenID-provider and intercepting the URLS for *authorization* and *logout* endpoints.
- `AuthServer__MetaAddress` is the `.well-known/openid-configuration` endpoint for issuing access_token and internal token validation. It is the containerized `http://bookstore-authserver` by default.
@ -1691,7 +1596,7 @@ bookstore-blazor:
- Kestrel__Certificates__Default__Password=91f91912-5ab0-49df-8166-23377efaf3cc
- App__SelfUrl=https://localhost:44314
- AuthServer__RequireHttpsMetadata=false {{ if Tiered == "Yes" }}
- AuthServer__IsContainerizedOnLocalhost=true
- AuthServer__IsOnK8s=true
- AuthServer__Authority=https://localhost:44334/
- AuthServer__MetaAddress=http://bookstore-authserver
- RemoteServices__Default__BaseUrl=http://bookstore-api
@ -1718,7 +1623,7 @@ This is the Blazor Server application Docker service is using the `acme/bookstor
The Blazor Server is a server-side rendering application that uses the **hybrid flow**. This flow uses **browser** to login/logout process to the OpenID-provider but issues the **access_token from the back-channel** (server-side). To achieve this functionality, the module class has extra `OpenIdConnectOptions` to override some of the events:
```csharp
if (Convert.ToBoolean(configuration["AuthServer:IsContainerizedOnLocalhost"]))
if (Convert.ToBoolean(configuration["AuthServer:IsOnK8s"]))
{
context.Services.Configure<OpenIdConnectOptions>("oidc", options =>
{
@ -1765,7 +1670,7 @@ if (Convert.ToBoolean(configuration["AuthServer:IsContainerizedOnLocalhost"]))
{{ if Tiered == "Yes" }}
- `AuthServer__IsContainerizedOnLocalhost` is the configuration to enable the **OpenIdConnectOptions** to provide a different endpoint for the MetaAddress of the OpenID-provider and intercept the URLS for *authorization* and *logout* endpoints.
- `AuthServer__IsOnK8s` is the configuration to enable the **OpenIdConnectOptions** to provide a different endpoint for the MetaAddress of the OpenID-provider and intercept the URLS for *authorization* and *logout* endpoints.
- `AuthServer__MetaAddress` is the `.well-known/openid-configuration` endpoint for issuing the access_token and internal token validation. It is the containerized `http://bookstore-authserver` by default.

4
docs/en/solution-templates/layered-web-application/solution-structure.md

@ -214,3 +214,7 @@ This project is an application that hosts the API of the solution. It has its ow
Just like the default structure, this project contains the User Interface (UI) of the application. It contains razor pages, JavaScript files, style files, images and so on...
This project contains an `appsettings.json` file, but this time it does not have a connection string because it never connects to the database. Instead, it mainly contains the endpoint of the remote API server and the authentication server.
### Docker Compose
The **docker-compose** configuration files in the `etc/docker-compose` folder is configured to run the solution with Docker. See [Docker Deployment using Docker Compose](deployment/deployment-docker-compose.md) for more information.

2
docs/en/studio/monitoring-applications.md

@ -48,7 +48,7 @@ public override void ConfigureServices(ServiceConfigurationContext context)
In this tab, you can view comprehensive overall information. You have the option to search by application name and filter by application state. To reset all filters, use the *Clear Filters* button. When you apply a filter header informations gonna refresh by filtered applications.
- `Apps Running`: The number of applications running. It includes CLI and C# applications. In the example two C# microservice applications and one CLI application is running.
- `Apps Running`: The number of applications running. It includes only C# applications. In the example, nine C# microservice applications are running.
- `Requests`: The number of HTTP requests received by all C# applications.
- `Events`: The number of [Distributed Event](../framework/infrastructure/event-bus/distributed) sent or received by all C# applications.
- `Exceptions`: The number of exceptions thrown by all C# applications.

8
docs/en/studio/running-applications.md

@ -230,3 +230,11 @@ CLI applications uses the [powershell](https://learn.microsoft.com/en-us/powersh
- `Remove`: This option allows you to delete the selected application.
> When CLI applications start chain icon won't be visible, because only C# applications can connect the ABP Studio.
## Docker Compose
You can manually run applications using [Docker Compose](https://docs.docker.com/compose/). This allows for easy setup and management of multi-container Docker applications. To get started, ensure you have Docker and Docker Compose installed on your machine.
Refer to the [Deployment with Docker Compose](../solution-templates/layered-web-application/deployment/deployment-docker-compose.md) documentation for detailed instructions on how to configure and run your applications using `docker-compose`.
> Note: The **Docker Compose** is not available in the ABP Studio interface.

BIN
docs/en/tutorials/modular-crm/images/abp-studio-ordering-swagger-ui-in-browser.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 54 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-swagger-ui-create-order-execute.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-swagger-ui-create-product-execute.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 27 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-swagger-ui-create-product-try.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

BIN
docs/en/tutorials/modular-crm/images/abp-studio-swagger-ui-in-browser.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 63 KiB

7
docs/en/tutorials/modular-crm/part-03.md

@ -387,7 +387,10 @@ Configure<AbpAspNetCoreMvcOptions>(options =>
options.ConventionalControllers.Create(typeof(ModularCrmModule).Assembly);
//ADD THE FOLLOWING LINE:
options.ConventionalControllers.Create(typeof(ProductsApplicationModule).Assembly);
options.ConventionalControllers.Create(typeof(ProductsApplicationModule).Assembly, settings =>
{
settings.RootPath = "products";
});
});
````
@ -409,7 +412,7 @@ Once you see the user interface of the web application, type `/swagger` at the e
![abp-studio-swagger-ui-in-browser](images/abp-studio-swagger-ui-in-browser.png)
Expand the `/api/app/product` API and click the *Try it out* button as shown in the following figure:
Expand the `/api/products/product` API and click the *Try it out* button as shown in the following figure:
![abp-studio-swagger-ui-create-product-try](images/abp-studio-swagger-ui-create-product-try.png)

16
docs/en/tutorials/modular-crm/part-05.md

@ -345,7 +345,9 @@ public class OrderAppService : OrderingAppService, IOrderAppService
}
````
Open the `ModularCrmModule` class in the main application's solution (the `ModularCrm` solution), find the `ConfigureAutoApiControllers` method and add the following lines inside that method:
### Exposing Application Services as HTTP API Controllers
After implementing the application service, now we need to create HTTP API endpoints for the ordering module. For that purpose, open the `ModularCrmModule` class in the main application's solution (the `ModularCrm` solution), find the `ConfigureAutoApiControllers` method and add the following lines inside that method:
````csharp
private void ConfigureAutoApiControllers()
@ -353,10 +355,16 @@ private void ConfigureAutoApiControllers()
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(ModularCrmModule).Assembly);
options.ConventionalControllers.Create(typeof(ProductsApplicationModule).Assembly);
options.ConventionalControllers.Create(typeof(ProductsApplicationModule).Assembly, settings =>
{
settings.RootPath = "products";
});
//ADD THE FOLLOWING LINE:
options.ConventionalControllers.Create(typeof(OrderingModule).Assembly);
options.ConventionalControllers.Create(typeof(OrderingModule).Assembly, settings =>
{
settings.RootPath = "orders";
});
});
}
````
@ -373,7 +381,7 @@ Once you see the user interface of the web application, type `/swagger` at the e
![abp-studio-ordering-swagger-ui-in-browser](images/abp-studio-ordering-swagger-ui-in-browser.png)
Expand the `/api/app/order` API and click the *Try it out* button. Then, create a few orders by filling in the request body and clicking the *Execute* button:
Expand the `/api/orders/order` API and click the *Try it out* button. Then, create a few orders by filling in the request body and clicking the *Execute* button:
![abp-studio-swagger-ui-create-order-execute](images/abp-studio-swagger-ui-create-order-execute.png)

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs

@ -106,11 +106,11 @@ public class AbpPaginationTagHelperService : AbpTagHelperService<AbpPaginationTa
protected virtual async Task<string> GetPreviousButtonAsync(TagHelperContext context, TagHelperOutput output)
{
var localizationKey = "PagerPrevious";
var currentPage = TagHelper.Model.CurrentPage == 1
? TagHelper.Model.CurrentPage.ToString()
: (TagHelper.Model.CurrentPage - 1).ToString();
var currentPage = TagHelper.Model.CurrentPage > 1
? (TagHelper.Model.CurrentPage - 1).ToString()
: "1";
return
"<li class=\"page-item " + (TagHelper.Model.CurrentPage == 1 ? "disabled" : "") + "\">\r\n" +
"<li class=\"page-item " + (TagHelper.Model.CurrentPage <= 1 ? "disabled" : "") + "\">\r\n" +
(await RenderAnchorTagHelperLinkHtmlAsync(context, output, currentPage, localizationKey)) + " </li>";
}

15
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs

@ -37,9 +37,9 @@ public class ChangeThemeStep : ProjectBuildPipelineStep
{
var defaultThemeName = context.BuildArgs.TemplateName is AppTemplate.TemplateName or AppNoLayersTemplate.TemplateName
? LeptonXLite : LeptonX;
new RemoveFilesStep($"/Themes/{defaultThemeName}").Execute(context);
ChangeThemeToBasicForMvcProjects(context, defaultThemeName);
ChangeThemeToBasicForBlazorProjects(context, defaultThemeName);
ChangeThemeToBasicForBlazorServerProjects(context, defaultThemeName);
@ -622,6 +622,11 @@ public class ChangeThemeStep : ProjectBuildPipelineStep
return;
}
if (!file.Content.Contains("ThemeModule"))
{
return;
}
file.NormalizeLineEndings();
var lines = file.GetLines().ToList();
@ -630,7 +635,7 @@ public class ChangeThemeStep : ProjectBuildPipelineStep
{
lines.AddFirst(@namespace);
}
file.SetLines(lines);
}
@ -641,7 +646,7 @@ public class ChangeThemeStep : ProjectBuildPipelineStep
".Web", ".AuthServer", ".Web.Public", ".Web.Public.Host",
"" //for app-nolayers-mvc
};
if(!context.Symbols.Contains("tiered"))
{
projectNames.Add(".HttpApi.Host");
@ -716,7 +721,7 @@ public class ChangeThemeStep : ProjectBuildPipelineStep
context,
$"_Host.cshtml",
$"{defaultThemeName}Theme.Components",
"BasicTheme.Themes.Basic"
"BasicTheme.Themes.Basic"
);
ReplaceAllKeywords(

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

@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Reflection;
using Volo.Abp.Reflection;
namespace Volo.Abp.Http.Modeling;
@ -46,7 +47,7 @@ public class TypeApiDescriptionModel
else
{
typeModel.Properties = type
.GetProperties()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.DeclaringType == type)
.Select(PropertyApiDescriptionModel.Create)
.ToArray();

4
modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml

@ -57,6 +57,10 @@
<div class="@containerClass">
<abp-row>
<abp-column class="col mx-auto" style="max-width: 440px">
<div class="brand-container">
<img class="brand-logo" src="@(BrandingProvider.LogoUrl ?? Url.Content("~/themes/basic/logo.svg"))" alt="@BrandingProvider.AppName"/>
<h2 class="brand-text">@BrandingProvider.AppName</h2>
</div>
@if (MultiTenancyOptions.Value.IsEnabled &&
(TenantResolveResultAccessor.Result?.AppliedResolvers?.Contains(CookieTenantResolveContributor.ContributorName) == true ||
TenantResolveResultAccessor.Result?.AppliedResolvers?.Contains(QueryStringTenantResolveContributor.ContributorName) == true))

24
modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/wwwroot/themes/basic/layout.css

@ -1,4 +1,6 @@
@import url('https://fonts.googleapis.com/css2?family=Lexend:wght@100..900&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
#main-navbar-tools a.dropdown-toggle {
text-decoration: none;
color: #fff;
@ -97,4 +99,24 @@ div.dataTables_wrapper div.dataTables_length label {
}
.rtl table.dataTable thead th, table.dataTable thead td, table.dataTable tfoot th, table.dataTable tfoot td {
text-align: right;
}
.brand-container{
text-align: center;
margin-top: 8rem;
}
.brand-logo {
height: 50px;
}
.brand-text {
color: #292D33;
font-family: Lexend;
font-size: 30px;
font-style: normal;
font-weight: 500;
line-height: 34px;
margin-top: 5px;
margin-bottom: 15px;
}

17
modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/wwwroot/themes/basic/logo.svg

@ -0,0 +1,17 @@
<svg width="147" height="50" viewBox="0 0 147 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_7065_14314)">
<path d="M25.8749 28.9393C28.1779 28.9393 30.0448 27.0724 30.0448 24.7695C30.0448 22.4665 28.1779 20.5996 25.8749 20.5996C23.572 20.5996 21.7051 22.4665 21.7051 24.7695C21.7051 27.0724 23.572 28.9393 25.8749 28.9393Z" fill="#B84297"/>
<path d="M74.2393 11.0308C72.0441 11.0115 69.8776 11.5309 67.9297 12.5435C65.9819 13.5561 64.3121 15.031 63.0668 16.8389C61.8215 18.6469 61.0386 20.7327 60.7868 22.9135C60.535 25.0944 60.822 27.3037 61.6225 29.3479C62.4231 31.392 63.7128 33.2087 65.3786 34.6385C67.0445 36.0684 69.0356 37.0678 71.1775 37.5492C73.3194 38.0307 75.5467 37.9794 77.6642 37.3999C79.7816 36.8204 81.7247 35.7304 83.3229 34.2255V36.6566H87.5314V25.0904C87.5421 24.8417 87.5485 24.5908 87.5464 24.3379C87.5047 20.8216 86.0893 17.4611 83.6027 14.9745C81.1161 12.4879 77.7556 11.0725 74.2393 11.0308ZM74.2972 33.7002C72.4534 33.7349 70.6413 33.2176 69.0937 32.2148C67.546 31.2119 66.3335 29.7693 65.6119 28.0722C64.8902 26.3751 64.6924 24.5011 65.0439 22.6907C65.3953 20.8804 66.2799 19.2164 67.584 17.9125C68.8882 16.6087 70.5524 15.7245 72.3628 15.3735C74.1732 15.0225 76.0473 15.2207 77.7442 15.9428C79.4411 16.6648 80.8835 17.8777 81.8859 19.4255C82.8884 20.9734 83.4053 22.7856 83.3701 24.6294C83.3701 24.7216 83.3701 24.8117 83.3572 24.9017H83.3251V25.3541C83.0966 27.6071 82.0527 29.699 80.3896 31.2361C78.7266 32.7732 76.5591 33.6495 74.2951 33.7002H74.2972Z" fill="#292D33"/>
<path d="M103.56 11.0306C100.084 10.9953 96.7302 12.3148 94.2102 14.7095V3.00391H90.0039V24.9015H90.0275C90.1154 27.5308 90.974 30.0762 92.4965 32.2215C94.0191 34.3669 96.1385 36.0175 98.5914 36.9684C101.044 37.9192 103.722 38.1283 106.293 37.5697C108.864 37.011 111.214 35.7093 113.051 33.8263C114.888 31.9432 116.131 29.5619 116.626 26.9781C117.121 24.3944 116.846 21.7222 115.835 19.2936C114.823 16.865 113.121 14.787 110.938 13.318C108.756 11.8489 106.19 11.0535 103.56 11.0306V11.0306ZM103.611 33.7C101.207 33.7458 98.8805 32.8537 97.1236 31.2128C95.3666 29.5719 94.3178 27.3112 94.1995 24.9101V24.9101V23.9689C94.2942 22.1709 94.9117 20.4396 95.9764 18.9876C97.041 17.5356 98.5065 16.426 100.193 15.795C101.879 15.164 103.713 15.0391 105.469 15.4355C107.226 15.832 108.828 16.7326 110.08 18.0268C111.331 19.3211 112.178 20.9526 112.516 22.7212C112.853 24.4897 112.667 26.3184 111.98 27.9828C111.294 29.6471 110.136 31.0747 108.649 32.0903C107.162 33.1059 105.411 33.6654 103.611 33.7V33.7Z" fill="#292D33"/>
<path d="M132.882 11.0312C131.099 11.0155 129.332 11.3549 127.682 12.0298C126.032 12.7046 124.534 13.7012 123.273 14.9615C122.013 16.2219 121.016 17.7206 120.341 19.3703C119.667 21.02 119.327 22.7875 119.343 24.5698C119.343 24.6813 119.343 24.7842 119.343 24.9021H119.319V46.8019H123.526V34.1744C125.112 35.6781 127.042 36.7713 129.148 37.359C131.253 37.9466 133.471 38.0108 135.607 37.5461C137.743 37.0813 139.733 36.1017 141.404 34.6924C143.075 33.2831 144.376 31.4868 145.195 29.4598C146.013 27.4328 146.324 25.2365 146.1 23.062C145.876 20.8875 145.124 18.8007 143.91 16.983C142.696 15.1654 141.055 13.672 139.132 12.6328C137.209 11.5937 135.061 11.0402 132.875 11.0205L132.882 11.0312ZM132.942 33.7006C130.543 33.7462 128.221 32.858 126.465 31.2235C124.71 29.589 123.658 27.3362 123.532 24.9407V24.9021C123.532 24.6878 123.517 24.4884 123.532 24.2783C123.569 22.4648 124.137 20.7021 125.168 19.2095C126.199 17.7169 127.645 16.5602 129.328 15.8835C131.011 15.2067 132.856 15.0397 134.633 15.4033C136.41 15.7669 138.041 16.6449 139.322 17.9282C140.604 19.2115 141.48 20.8434 141.842 22.6208C142.204 24.3983 142.034 26.2428 141.356 27.9248C140.677 29.6069 139.519 31.0523 138.025 32.0812C136.531 33.11 134.768 33.6769 132.954 33.7114L132.942 33.7006Z" fill="#292D33"/>
<path d="M25.8762 0.00294068C22.0878 -0.00061638 18.3485 0.859427 14.9424 2.51771V7.88814C17.9771 5.95077 21.472 4.85417 25.0695 4.71047C28.6671 4.56676 32.2383 5.38111 35.4179 7.07025C38.5975 8.75938 41.2714 11.2627 43.1662 14.3242C45.0611 17.3856 46.1088 20.8954 46.2023 24.4946V24.4946V39.6004C48.8844 35.8623 50.4836 31.4569 50.824 26.8687C51.1645 22.2805 50.2329 17.6873 48.1319 13.5943C46.0309 9.50126 42.8417 6.06695 38.9152 3.66907C34.9887 1.27119 30.477 0.00264244 25.8762 0.00294068V0.00294068Z" fill="#292D33"/>
<path d="M25.8756 45.3391C20.6536 45.3389 15.6321 43.3283 11.8531 39.7245C8.07398 36.1207 5.82741 31.2002 5.57948 25.9841H5.5559V10.4346C1.90165 15.5416 0.302109 21.8363 1.07501 28.0683C1.84791 34.3002 4.9367 40.0135 9.7278 44.0731C14.5189 48.1327 20.6617 50.2416 26.936 49.9808C33.2104 49.7201 39.157 47.1088 43.5948 42.6657V34.9692C41.8282 38.1154 39.2555 40.7343 36.1414 42.5569C33.0272 44.3794 29.4839 45.3397 25.8756 45.3391Z" fill="#292D33"/>
<path d="M25.875 31.4199C24.3641 31.4213 22.8965 30.9155 21.7073 29.9835V34.7644C23.8753 35.6561 26.2725 35.8274 28.5452 35.2531C30.818 34.6788 32.846 33.3892 34.3301 31.5746C35.8142 29.7599 36.6757 27.5164 36.7877 25.1748C36.8997 22.8333 36.2562 20.5178 34.952 18.5698C33.6479 16.6219 31.7522 15.1446 29.5446 14.356C27.337 13.5674 24.9344 13.5092 22.6912 14.1899C20.448 14.8707 18.483 16.2544 17.0861 18.1369C15.6891 20.0194 14.9342 22.3011 14.9326 24.6453V38.7027C16.2039 39.7036 17.6061 40.5259 19.1003 41.1467V24.6388C19.1003 23.2989 19.4977 21.9891 20.2421 20.875C20.9865 19.761 22.0445 18.8926 23.2824 18.3799C24.5203 17.8671 25.8825 17.733 27.1967 17.9944C28.5108 18.2558 29.7179 18.901 30.6654 19.8484C31.6129 20.7959 32.2581 22.003 32.5195 23.3172C32.7809 24.6313 32.6467 25.9935 32.134 27.2314C31.6212 28.4693 30.7529 29.5274 29.6388 30.2718C28.5247 31.0162 27.2149 31.4135 25.875 31.4135V31.4199Z" fill="#292D33"/>
<path d="M12.3286 36.1851V24.7689C12.3299 21.9102 13.2359 19.1252 14.9166 16.8127C16.5974 14.5003 18.9668 12.779 21.6856 11.8954C24.4044 11.0118 27.333 11.0112 30.0521 11.8937C32.7713 12.7762 35.1414 14.4965 36.8231 16.8082C38.5049 19.12 39.4119 21.9046 39.4144 24.7633C39.4169 27.6221 38.5147 30.4083 36.837 32.723C35.1593 35.0377 32.7922 36.7621 30.0746 37.6493C27.3571 38.5366 24.4284 38.5411 21.7081 37.6622V41.9929C24.7756 42.7356 27.9858 42.6456 31.0068 41.732C34.0278 40.8185 36.75 39.1146 38.8919 36.7966C41.0338 34.4786 42.5178 31.6305 43.1903 28.5469C43.8628 25.4633 43.6994 22.256 42.7172 19.2566C41.7349 16.2572 39.9693 13.5747 37.603 11.4862C35.2367 9.39778 32.3555 7.97928 29.2573 7.37737C26.1591 6.77546 22.9564 7.01198 19.9802 8.06249C17.004 9.113 14.3625 10.9394 12.3286 13.3528V7.56428V3.98828C10.8199 4.95977 9.4198 6.09035 8.15234 7.36061V25.3006C8.27063 29.2972 9.74319 33.1351 12.3286 36.1851V36.1851Z" fill="#292D33"/>
</g>
<defs>
<clipPath id="clip0_7065_14314">
<rect width="145.226" height="50" fill="white" transform="translate(0.886719)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

6
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Users/CmsUserDto.cs

@ -1,4 +1,4 @@
using System;
using System;
using Volo.Abp.Application.Dtos;
namespace Volo.CmsKit.Users;
@ -6,9 +6,9 @@ namespace Volo.CmsKit.Users;
[Serializable]
public class CmsUserDto : ExtensibleEntityDto<Guid>
{
public virtual Guid? TenantId { get; protected set; }
public virtual Guid? TenantId { get; set; }
public virtual string UserName { get; protected set; }
public virtual string UserName { get; set; }
public virtual string Name { get; set; }

2
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs

@ -69,6 +69,7 @@ public interface IIdentityUserRepository : IBasicRepository<IdentityUser, Guid>
bool includeDetails = false,
Guid? roleId = null,
Guid? organizationUnitId = null,
Guid? id = null,
string userName = null,
string phoneNumber = null,
string emailAddress = null,
@ -114,6 +115,7 @@ public interface IIdentityUserRepository : IBasicRepository<IdentityUser, Guid>
string filter = null,
Guid? roleId = null,
Guid? organizationUnitId = null,
Guid? id = null,
string userName = null,
string phoneNumber = null,
string emailAddress = null,

58
modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs

@ -62,35 +62,35 @@ public class EfCoreIdentityUserRepository : EfCoreRepository<IIdentityDbContext,
{
var dbContext = await GetDbContextAsync();
var userRoles = await (from userRole in dbContext.Set<IdentityUserRole>()
join role in dbContext.Roles on userRole.RoleId equals role.Id
where userIds.Contains(userRole.UserId)
group new
{
userRole.UserId,
role.Name
} by userRole.UserId
join role in dbContext.Roles on userRole.RoleId equals role.Id
where userIds.Contains(userRole.UserId)
group new {
userRole.UserId,
role.Name
} by userRole.UserId
into gp
select new IdentityUserIdWithRoleNames
{
Id = gp.Key, RoleNames = gp.Select(x => x.Name).ToArray()
}).ToListAsync(cancellationToken: cancellationToken);
select new IdentityUserIdWithRoleNames
{
Id = gp.Key,
RoleNames = gp.Select(x => x.Name).ToArray()
}).ToListAsync(cancellationToken: cancellationToken);
var orgUnitRoles = await (from userOu in dbContext.Set<IdentityUserOrganizationUnit>()
join roleOu in dbContext.Set<OrganizationUnitRole>() on userOu.OrganizationUnitId equals roleOu.OrganizationUnitId
join role in dbContext.Roles on roleOu.RoleId equals role.Id
where userIds.Contains(userOu.UserId)
group new
{
userOu.UserId,
role.Name
} by userOu.UserId
join roleOu in dbContext.Set<OrganizationUnitRole>() on userOu.OrganizationUnitId equals roleOu.OrganizationUnitId
join role in dbContext.Roles on roleOu.RoleId equals role.Id
where userIds.Contains(userOu.UserId)
group new {
userOu.UserId,
role.Name
} by userOu.UserId
into gp
select new IdentityUserIdWithRoleNames
{
Id = gp.Key, RoleNames = gp.Select(x => x.Name).ToArray()
}).ToListAsync(cancellationToken: cancellationToken);
select new IdentityUserIdWithRoleNames
{
Id = gp.Key,
RoleNames = gp.Select(x => x.Name).ToArray()
}).ToListAsync(cancellationToken: cancellationToken);
return userRoles.Concat(orgUnitRoles).GroupBy(x => x.Id).Select(x => new IdentityUserIdWithRoleNames {Id = x.Key, RoleNames = x.SelectMany(y => y.RoleNames).Distinct().ToArray()}).ToList();
return userRoles.Concat(orgUnitRoles).GroupBy(x => x.Id).Select(x => new IdentityUserIdWithRoleNames { Id = x.Key, RoleNames = x.SelectMany(y => y.RoleNames).Distinct().ToArray() }).ToList();
}
public virtual async Task<List<string>> GetRoleNamesInOrganizationUnitAsync(
@ -196,6 +196,7 @@ public class EfCoreIdentityUserRepository : EfCoreRepository<IIdentityDbContext,
bool includeDetails = false,
Guid? roleId = null,
Guid? organizationUnitId = null,
Guid? id = null,
string userName = null,
string phoneNumber = null,
string emailAddress = null,
@ -215,6 +216,7 @@ public class EfCoreIdentityUserRepository : EfCoreRepository<IIdentityDbContext,
filter,
roleId,
organizationUnitId,
id,
userName,
phoneNumber,
emailAddress,
@ -272,6 +274,7 @@ public class EfCoreIdentityUserRepository : EfCoreRepository<IIdentityDbContext,
string filter = null,
Guid? roleId = null,
Guid? organizationUnitId = null,
Guid? id = null,
string userName = null,
string phoneNumber = null,
string emailAddress = null,
@ -291,6 +294,7 @@ public class EfCoreIdentityUserRepository : EfCoreRepository<IIdentityDbContext,
filter,
roleId,
organizationUnitId,
id,
userName,
phoneNumber,
emailAddress,
@ -434,6 +438,7 @@ public class EfCoreIdentityUserRepository : EfCoreRepository<IIdentityDbContext,
string filter = null,
Guid? roleId = null,
Guid? organizationUnitId = null,
Guid? id = null,
string userName = null,
string phoneNumber = null,
string emailAddress = null,
@ -451,6 +456,11 @@ public class EfCoreIdentityUserRepository : EfCoreRepository<IIdentityDbContext,
{
var upperFilter = filter?.ToUpperInvariant();
var query = await GetQueryableAsync();
if (id.HasValue)
{
return query.Where(x => x.Id == id);
}
if (roleId.HasValue)
{

15
modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs

@ -164,6 +164,7 @@ public class MongoIdentityUserRepository : MongoDbRepository<IAbpIdentityMongoDb
bool includeDetails = false,
Guid? roleId = null,
Guid? organizationUnitId = null,
Guid? id = null,
string userName = null,
string phoneNumber = null,
string emailAddress = null,
@ -183,6 +184,7 @@ public class MongoIdentityUserRepository : MongoDbRepository<IAbpIdentityMongoDb
filter,
roleId,
organizationUnitId,
id,
userName,
phoneNumber,
emailAddress,
@ -243,6 +245,7 @@ public class MongoIdentityUserRepository : MongoDbRepository<IAbpIdentityMongoDb
string filter = null,
Guid? roleId = null,
Guid? organizationUnitId = null,
Guid? id = null,
string userName = null,
string phoneNumber = null,
string emailAddress = null,
@ -262,6 +265,7 @@ public class MongoIdentityUserRepository : MongoDbRepository<IAbpIdentityMongoDb
filter,
roleId,
organizationUnitId,
id,
userName,
phoneNumber,
emailAddress,
@ -398,6 +402,7 @@ public class MongoIdentityUserRepository : MongoDbRepository<IAbpIdentityMongoDb
var allOrganizationUnitRoleIds = organizationUnitAndRoleIds.SelectMany(x => x.Roles.Select(r => r.RoleId)).ToList();
var allRoleIds = roleIds.Union(allOrganizationUnitRoleIds);
var roles = await (await GetQueryableAsync<IdentityRole>(cancellationToken)).Where(r => allRoleIds.Contains(r.Id)).Select(r => new{ r.Id, r.Name }).ToListAsync(cancellationToken);
var userRoles = userAndRoleIds.ToDictionary(x => x.Key, x => roles.Where(r => x.Value.Contains(r.Id)).Select(r => r.Name).ToArray());
@ -412,9 +417,9 @@ public class MongoIdentityUserRepository : MongoDbRepository<IAbpIdentityMongoDb
{
user.RoleNames = user.RoleNames.Union(roleNames).ToArray();
}
else if(roleNames.Any())
else if (roleNames.Any())
{
result.Add(new IdentityUserIdWithRoleNames { Id = userAndOrganizationUnitId.Key, RoleNames = roleNames});
result.Add(new IdentityUserIdWithRoleNames { Id = userAndOrganizationUnitId.Key, RoleNames = roleNames });
}
}
@ -425,6 +430,7 @@ public class MongoIdentityUserRepository : MongoDbRepository<IAbpIdentityMongoDb
string filter = null,
Guid? roleId = null,
Guid? organizationUnitId = null,
Guid? id = null,
string userName = null,
string phoneNumber = null,
string emailAddress = null,
@ -443,6 +449,11 @@ public class MongoIdentityUserRepository : MongoDbRepository<IAbpIdentityMongoDb
var upperFilter = filter?.ToUpperInvariant();
var query = await GetQueryableAsync(cancellationToken);
if (id.HasValue)
{
return query.Where(x => x.Id == id);
}
if (roleId.HasValue)
{
var organizationUnitIds = (await GetQueryableAsync<OrganizationUnit>(cancellationToken))

26
npm/ng-packs/packages/schematics/src/utils/service.ts

@ -18,6 +18,7 @@ import {
createTypeAdapter,
createTypeParser,
createTypesToImportsReducer,
getTypeForEnumList,
removeTypeModifiers,
} from './type';
import { eBindingSourceId } from '../enums';
@ -80,7 +81,19 @@ export function createActionToBodyMapper() {
const adaptType = createTypeAdapter();
return ({ httpMethod, parameters, returnValue, url }: Action) => {
const responseType = adaptType(returnValue.typeSimple);
let responseType = adaptType(returnValue.typeSimple);
if (responseType.includes('enum')) {
const type = returnValue.typeSimple.replace('enum', returnValue.type);
if (responseType === 'enum') {
responseType = adaptType(type);
}
if (responseType === 'enum[]') {
const normalizedType = getTypeForEnumList(type);
responseType = adaptType(normalizedType);
}
}
const responseTypeWithNamespace = returnValue.typeSimple;
const body = new Body({ method: httpMethod, responseType, url, responseTypeWithNamespace });
@ -109,7 +122,12 @@ export function createActionToSignatureMapper() {
if (isFormData || isFormArray) {
return new Property({ name: p.name, type: 'FormData' });
}
const type = adaptType(p.typeSimple);
let type = adaptType(p.typeSimple);
if (p.typeSimple === 'enum' || p.typeSimple === '[enum]') {
type = adaptType(p.type);
}
const parameter = new Property({ name: p.name, type });
parameter.setDefault(p.defaultValue);
parameter.setOptional(p.isOptional);
@ -183,7 +201,9 @@ function createActionToImportsReducer(
parseGenerics(paramType)
.toGenerics()
.forEach(type => {
if (types[type]) acc.push({ type, isEnum: types[type].isEnum });
if (types[type]) {
acc.push({ type, isEnum: types[type].isEnum });
}
}),
);

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

@ -58,6 +58,10 @@ export function removeTypeModifiers(type: string) {
return type.replace(/\[\]/g, '');
}
export function getTypeForEnumList(type: string) {
return type.replace(/^.*<([^>]+)>.*$/, '[$1]');
}
export function createTypesToImportsReducer(solution: string, namespace: string) {
const mapTypeToImport = createTypeToImportMapper(solution, namespace);
@ -68,7 +72,7 @@ export function createTypesToImportsReducer(solution: string, namespace: string)
return;
}
if(newImport.specifiers.some(f => f.toLocaleLowerCase() === type.toLocaleLowerCase())){
if (newImport.specifiers.some(f => f.toLocaleLowerCase() === type.toLocaleLowerCase())) {
return;
}
@ -76,7 +80,7 @@ export function createTypesToImportsReducer(solution: string, namespace: string)
({ keyword, path }) => keyword === newImport.keyword && path === newImport.path,
);
if (!existingImport){
if (!existingImport) {
return imports.push(newImport);
}
@ -101,7 +105,7 @@ export function createTypeToImportMapper(solution: string, namespace: string) {
const refs = [removeTypeModifiers(type)];
const specifiers = [adaptType(simplifyType(refs[0]).split('<')[0])];
let path = relativePathToModel(namespace, modelNamespace);
if (VOLO_REGEX.test(type)) {
path = '@abp/ng.core';
}

4
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj

@ -8,8 +8,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.2" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.3" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.3" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Sinks.Async" Version="2.0.0" />
</ItemGroup>

4
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj

@ -8,8 +8,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.2" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.3" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.3" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Sinks.Async" Version="2.0.0" />
</ItemGroup>

4
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj

@ -9,8 +9,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.2" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.3" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.0" />
</ItemGroup>

4
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyCompanyName.MyProjectName.Blazor.Client.csproj

@ -12,8 +12,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.2" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.3" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.0" />
</ItemGroup>

4
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj

@ -14,8 +14,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.2" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.3" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.3" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Sinks.Async" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.0" />

4
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj

@ -15,8 +15,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.0" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.2" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.3" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.3" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Sinks.Async" Version="2.0.0" />
</ItemGroup>

4
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Client.csproj

@ -13,8 +13,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.2" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.3" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.0" />
</ItemGroup>

4
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client.csproj

@ -13,8 +13,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.2" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.3" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.0" />
</ItemGroup>

4
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.csproj

@ -16,8 +16,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.0" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.2" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.3" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.3" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Sinks.Async" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.0" />

4
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyCompanyName.MyProjectName.Blazor.WebApp.csproj

@ -16,8 +16,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.0" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.2" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.3" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.3" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Sinks.Async" Version="2.0.0" />
</ItemGroup>

4
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/MyCompanyName.MyProjectName.Blazor.Host.Client.csproj

@ -10,8 +10,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.2" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.3" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.0" />
</ItemGroup>

4
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj

@ -13,8 +13,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.2" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.7.3" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.7.3" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Sinks.Async" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0" />

Loading…
Cancel
Save