+ {
+ new PaymentRequestProductCreateDto
+ {
+ Code = "Product_01",
+ Name = "LEGO Super Mario",
+ Count = 1,
+ UnitPrice = 60,
+ TotalPrice = 60
+ }
+ },
+ ExtraProperties = new ExtraPropertyDictionary
+ {
+ // For Iyzico - Customer information
+ { "Name", "John" },
+ { "Surname", "Doe" },
+ { "Email", "john.doe@example.com" },
+ { "Address", "123 Main St" },
+ { "City", "Istanbul" },
+ { "Country", "Turkey" },
+ { "ZipCode", "34000" },
+
+ // For PayU - Customer information
+ { "BuyerName", "John" },
+ { "BuyerSurname", "Doe" },
+ { "BuyerEmail", "john.doe@example.com" }
+ }
+ });
+```
+
+#### Handling the Callback (Optional)
+
+When a user completes a payment on the external payment gateway, the following flow occurs:
+
+1. The user is redirected to the **PostPayment page** (handled internally by the payment module)
+2. The PostPayment page validates the payment with the gateway and updates the payment request status to **Completed**
+3. If a `CallbackUrl` is configured in `PaymentBlazorOptions`, the user is then redirected to that URL with the `paymentRequestId` as a query parameter
+
+Create a page to handle this callback and perform any application-specific actions:
+
+```csharp
+@page "/PaymentSucceed"
+@using Microsoft.AspNetCore.WebUtilities
+
+Payment Successful!
+Thank you for your purchase.
+Payment Request ID: @PaymentRequestId
+
+@code {
+ [Parameter]
+ [SupplyParameterFromQuery]
+ public Guid? PaymentRequestId { get; set; }
+
+ protected override async Task OnInitializedAsync()
+ {
+ if (PaymentRequestId.HasValue)
+ {
+ // The payment is already completed at this point.
+ // Perform application-specific actions here:
+ // e.g., activate subscription, send confirmation email,
+ // update order status, grant access to purchased content, etc.
+ }
+ }
+}
+```
+
+> **Note:** By the time the user reaches your callback page, the payment request status has already been set to **Completed** by the PostPayment page. Your callback page is for performing additional application-specific logic. It is also your responsibility to handle if a payment request is used more than once. If you have already delivered your product for a given `PaymentRequestId`, you should not deliver it again when the callback URL is visited a second time.
### Angular UI
-#### Installation
+For Angular applications, you need to read and apply the steps explained in the following sections:
+
+#### Configurations
In order to configure the application to use the payment module, you first need to import `PaymentAdminConfigModule` from `@volo/abp.ng.payment/admin/config` to the root configuration. `PaymentAdminConfigModule` has a static `forRoot` method which you should call for a proper configuration:
@@ -120,15 +387,35 @@ const APP_ROUTES: Routes = [
];
```
-#### Payment plans page
+### Pages
+
+#### Public Pages
+
+##### Payment Gateway Selection
+
+This page allows selecting a payment gateway. If there is only one payment gateway configured for the application, this page will be skipped.
+
+
+
+##### PrePayment Page
+
+Some payment gateways require additional information before redirecting to the payment gateway. For example, PayU and Iyzico require customer information (Name, Surname, Email Address, etc.) before processing the payment.
+
+
+
+#### Admin Pages
+
+##### Payment Plans Page
+
Payment plans for subscriptions can be managed on this page. You can connect external subscriptions for each gateway to a plan.


-#### Payment request list
-This page lists all the payment request operations in application.
+##### Payment Request List
+
+This page lists all the payment request operations in the application.

@@ -171,7 +458,22 @@ Configure(options =>
* ```PrePaymentUrl```: URL of the page before redirecting user to payment gateway for payment.
* ```PostPaymentUrl```: URL of the page when user redirected back from payment gateway to your website. This page is used to validate the payment mostly.
* ```Order```: Order of payment gateway for gateway selection page.
- * ```Recommended```: Is payment gateway is recommended or not. This information is displayed on payment gateway selection page.
+ * ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
+ * ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
+
+### PaymentBlazorOptions
+
+```PaymentBlazorOptions``` is used to configure Blazor application related configurations. This is the Blazor equivalent of `PaymentWebOptions`.
+
+* ```CallbackUrl```: Final callback URL for internal payment gateway modules to return. User will be redirected to this URL on your website after a successful payment.
+* ```RootUrl```: Root URL of your Blazor application.
+* ```GatewaySelectionCheckoutButtonStyle```: CSS style to add to the Checkout button on the gateway selection page. This class can be used for tracking user activity via 3rd party tools like Google Tag Manager.
+* ```PaymentGatewayBlazorConfigurationDictionary```: Used to store Blazor related payment gateway configuration.
+ * ```Name```: Name of payment gateway.
+ * ```PrePaymentUrl```: URL of the Blazor page before redirecting user to payment gateway for payment.
+ * ```PostPaymentUrl```: URL of the Blazor page when user is redirected back from payment gateway to your website.
+ * ```Order```: Order of payment gateway for gateway selection page.
+ * ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
* ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
### PayuOptions
@@ -193,9 +495,17 @@ Configure(options =>
```PayuWebOptions``` is used to configure PayU payment gateway web options.
-* ```Recommended```: Is payment gateway is recommended or not. This information is displayed on payment gateway selection page.
+* ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
* ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
-* ```PrePaymentCheckoutButtonStyle```: Css style to add Checkout button on PayU prepayment page. This class can be used for tracking user activity via 3rd party tools like Google Tag Manager.
+* ```PrePaymentCheckoutButtonStyle```: CSS style to add to the Checkout button on the PayU prepayment page. This class can be used for tracking user activity via 3rd party tools like Google Tag Manager.
+
+### PayuBlazorOptions
+
+```PayuBlazorOptions``` is used to configure PayU payment gateway Blazor options.
+
+* ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
+* ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
+* ```PrePaymentCheckoutButtonStyle```: CSS style to add to the Checkout button on the PayU prepayment page.
### TwoCheckoutOptions
@@ -210,7 +520,14 @@ Configure(options =>
```TwoCheckoutWebOptions``` is used to configure TwoCheckout payment gateway web options.
-* ```Recommended```: Is payment gateway is recommended or not. This information is displayed on payment gateway selection page.
+* ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
+* ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
+
+### TwoCheckoutBlazorOptions
+
+```TwoCheckoutBlazorOptions``` is used to configure TwoCheckout payment gateway Blazor options.
+
+* ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
* ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
### StripeOptions
@@ -228,7 +545,14 @@ Configure(options =>
```StripeWebOptions``` is used to configure Stripe payment gateway web options.
-* ```Recommended```: Is payment gateway is recommended or not. This information is displayed on payment gateway selection page.
+* ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
+* ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
+
+### StripeBlazorOptions
+
+```StripeBlazorOptions``` is used to configure Stripe payment gateway Blazor options.
+
+* ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
* ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
### PayPalOptions
@@ -245,7 +569,14 @@ Configure(options =>
```PayPalWebOptions``` is used to configure PayPal payment gateway web options.
-* ```Recommended```: Is payment gateway is recommended or not. This information is displayed on payment gateway selection page.
+* ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
+* ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
+
+### PayPalBlazorOptions
+
+```PayPalBlazorOptions``` is used to configure PayPal payment gateway Blazor options.
+
+* ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
* ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
### IyzicoOptions
@@ -263,9 +594,17 @@ Configure(options =>
```IyzicoWebOptions``` is used to configure Iyzico payment gateway web options.
-* ```Recommended```: Is payment gateway is recommended or not. This information is displayed on payment gateway selection page.
+* ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
+* ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
+* ```PrePaymentCheckoutButtonStyle```: CSS style to add to the Checkout button on the Iyzico prepayment page. This class can be used for tracking user activity via 3rd party tools like Google Tag Manager.
+
+### IyzicoBlazorOptions
+
+```IyzicoBlazorOptions``` is used to configure Iyzico payment gateway Blazor options.
+
+* ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
* ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
-* ```PrePaymentCheckoutButtonStyle```: CSS style to add Checkout button on Iyzico prepayment page. This class can be used for tracking user activity via 3rd party tools like Google Tag Manager.
+* ```PrePaymentCheckoutButtonStyle```: CSS style to add to the Checkout button on the Iyzico prepayment page.
### AlipayOptions
@@ -285,9 +624,17 @@ Configure(options =>
#### AlipayWebOptions
-* ```Recommended```: Is payment gateway is recommended or not. This information is displayed on payment gateway selection page.
+* ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
+* ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
+* ```PrePaymentCheckoutButtonStyle```: CSS style to add to the Checkout button on the Alipay prepayment page. This class can be used for tracking user activity via 3rd party tools like Google Tag Manager.
+
+#### AlipayBlazorOptions
+
+```AlipayBlazorOptions``` is used to configure Alipay payment gateway Blazor options.
+
+* ```Recommended```: Is payment gateway recommended or not. This information is displayed on payment gateway selection page.
* ```ExtraInfos```: List of informative strings for payment gateway. These texts are displayed on payment gateway selection page.
-* ```PrePaymentCheckoutButtonStyle```: CSS style to add Checkout button on Iyzico prepayment page. This class can be used for tracking user activity via 3rd party tools like Google Tag Manager.
+* ```PrePaymentCheckoutButtonStyle```: CSS style to add to the Checkout button on the Alipay prepayment page.
> You can check the [Alipay document](https://opendocs.alipay.com/open/02np97) for more details.
@@ -541,7 +888,7 @@ PrePayment page asks users for extra information if requested by the external pa
PostPayment page is responsible for validation of the response of the external payment gateway. When a user completes the payment, user is redirected to PostPayment page for that payment gateway and PostPayment page validates the status of the payment. If the payment is succeeded, status of the payment request is updated and user is redirected to main application.
-Note: It is main application's responsibility to handle if a payment request is used more than one time. For example if PostPayment page generates a URL like https://mywebsite.com/PaymentSucceed?PaymentRequestId={PaymentRequestId}, this URL can be visited more than onec manually by end users. If you already delivered your product for a given PaymentRequestId, you shouldn't deliver it when this URL is visited second time.
+Note: It is the main application's responsibility to handle if a payment request is used more than once. For example, if the PostPayment page generates a URL like https://mywebsite.com/PaymentSucceed?PaymentRequestId={PaymentRequestId}, this URL can be visited more than once manually by end users. If you have already delivered your product for a given PaymentRequestId, you shouldn't deliver it when this URL is visited a second time.
### Creating One-Time Payment
@@ -563,7 +910,7 @@ public class IndexModel: PageModel
{
var paymentRequest = await _paymentRequestAppService.CreateAsync(new PaymentRequestCreateDto()
{
- Currency= "USD",
+ Currency = "USD",
Products = new List()
{
new PaymentRequestProductCreateDto
@@ -582,7 +929,7 @@ public class IndexModel: PageModel
}
```
-If the payment is successful, payment module will return to configured ```PaymentWebOptions.CallbackUrl```. The main application can take necessary actions for a successful payment (Activating a user account, triggering a shipment start process etc...).
+If the payment is successful, payment module will return to the configured ```PaymentWebOptions.CallbackUrl```. The main application can take necessary actions for a successful payment (activating a user account, triggering a shipment start process, etc.).
## Subscriptions
diff --git a/docs/en/modules/permission-management.md b/docs/en/modules/permission-management.md
index 85cefffe7d..2e5f997423 100644
--- a/docs/en/modules/permission-management.md
+++ b/docs/en/modules/permission-management.md
@@ -7,9 +7,9 @@
# Permission Management Module
-This module implements the `IPermissionStore` to store and manage permissions values in a database.
+This module implements the `IPermissionStore` and `IResourcePermissionStore` to store and manage permission values in a database.
-> This document covers only the permission management module which persists permission values to a database. See the [Authorization document](../framework/fundamentals/authorization.md) to understand the authorization and permission systems.
+> This document covers only the permission management module which persists permission values to a database. See the [Authorization document](../framework/fundamentals/authorization/index.md) to understand the authorization and permission systems.
## How to Install
@@ -33,11 +33,87 @@ When you click *Actions* -> *Permissions* for a role, the permission management
In this dialog, you can grant permissions for the selected role. The tabs in the left side represents main permission groups and the right side contains the permissions defined in the selected group.
+### Resource Permission Management Dialog
+
+In addition to standard permissions, this module provides a reusable dialog for managing **resource-based permissions** on specific resource instances. This allows administrators to grant or revoke permissions for users, roles and clients on individual resources (e.g., a specific document, project, or any entity).
+
+
+
+The dialog displays all resource permissions defined for the resource type and allows granting them to specific users, roles or clients for the selected resource instance.
+
+You can integrate this dialog into your own application to manage permissions for your custom entities and resources. See the following sections to learn how to use the component in each UI framework.
+
+#### MVC / Razor Pages
+
+Use the `abp.ModalManager` to open the resource permission management dialog:
+
+````javascript
+var _resourcePermissionsModal = new abp.ModalManager(
+ abp.appPath + 'AbpPermissionManagement/ResourcePermissionManagementModal'
+);
+
+// Open the modal for a specific resource
+_resourcePermissionsModal.open({
+ resourceName: 'MyApp.Document',
+ resourceKey: documentId,
+ resourceDisplayName: documentTitle
+});
+````
+
+#### Blazor
+
+Use the `ResourcePermissionManagementModal` component's `OpenAsync` method to open the dialog:
+
+````razor
+@using Volo.Abp.PermissionManagement.Blazor.Components
+
+
+
+@code {
+ private ResourcePermissionManagementModal ResourcePermissionModal { get; set; }
+
+ private async Task OpenPermissionsModal()
+ {
+ await ResourcePermissionModal.OpenAsync(
+ resourceName: "MyApp.Document",
+ resourceKey: Document.Id.ToString(),
+ resourceDisplayName: Document.Title
+ );
+ }
+}
+````
+
+#### Angular
+
+Use the `ResourcePermissionManagementComponent`:
+
+````typescript
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { ResourcePermissionManagementComponent } from '@abp/ng.permission-management';
+
+@Component({
+ // ...
+})
+export class DocumentListComponent {
+ constructor(private modalService: NgbModal) {}
+
+ openPermissionsModal(document: DocumentDto) {
+ const modalRef = this.modalService.open(
+ ResourcePermissionManagementComponent,
+ { size: 'lg' }
+ );
+ modalRef.componentInstance.resourceName = 'MyApp.Document';
+ modalRef.componentInstance.resourceKey = document.id;
+ modalRef.componentInstance.resourceDisplayName = document.title;
+ }
+}
+````
+
## IPermissionManager
-`IPermissionManager` is the main service provided by this module. It is used to read and change the permission values. `IPermissionManager` is typically used by the *Permission Management Dialog*. However, you can inject it if you need to set a permission value.
+`IPermissionManager` is the main service provided by this module. It is used to read and change the global permission values. `IPermissionManager` is typically used by the *Permission Management Dialog*. However, you can inject it if you need to set a permission value.
-> If you just want to read/check permission values for the current user, use the `IAuthorizationService` or the `[Authorize]` attribute as explained in the [Authorization document](../framework/fundamentals/authorization.md).
+> If you just want to read/check permission values for the current user, use the `IAuthorizationService` or the `[Authorize]` attribute as explained in the [Authorization document](../framework/fundamentals/authorization/index.md).
**Example: Grant permissions to roles and users using the `IPermissionManager` service**
@@ -67,9 +143,125 @@ public class MyService : ITransientDependency
}
````
+## IResourcePermissionManager
+
+`IResourcePermissionManager` is the service for programmatically managing resource-based permissions. It is typically used by the *Resource Permission Management Dialog*. However, you can inject it when you need to grant, revoke, or query permissions for specific resource instances.
+
+> If you just want to check resource permission values for the current user, use the `IResourcePermissionChecker` service as explained in the [Resource-Based Authorization](../framework/fundamentals/authorization/resource-based-authorization.md) document.
+
+**Example: Grant and revoke resource permissions programmatically**
+
+````csharp
+public class DocumentPermissionService : ITransientDependency
+{
+ private readonly IResourcePermissionManager _resourcePermissionManager;
+
+ public DocumentPermissionService(
+ IResourcePermissionManager resourcePermissionManager)
+ {
+ _resourcePermissionManager = resourcePermissionManager;
+ }
+
+ public async Task GrantViewPermissionToUserAsync(
+ Guid documentId,
+ Guid userId)
+ {
+ await _resourcePermissionManager.SetAsync(
+ permissionName: "MyApp_Document_View",
+ resourceName: "MyApp.Document",
+ resourceKey: documentId.ToString(),
+ providerName: "U", // User
+ providerKey: userId.ToString(),
+ isGranted: true
+ );
+ }
+
+ public async Task GrantEditPermissionToRoleAsync(
+ Guid documentId,
+ string roleName)
+ {
+ await _resourcePermissionManager.SetAsync(
+ permissionName: "MyApp_Document_Edit",
+ resourceName: "MyApp.Document",
+ resourceKey: documentId.ToString(),
+ providerName: "R", // Role
+ providerKey: roleName,
+ isGranted: true
+ );
+ }
+
+ public async Task RevokeUserPermissionsAsync(
+ Guid documentId,
+ Guid userId)
+ {
+ await _resourcePermissionManager.DeleteAsync(
+ resourceName: "MyApp.Document",
+ resourceKey: documentId.ToString(),
+ providerName: "U",
+ providerKey: userId.ToString()
+ );
+ }
+}
+````
+
+## IResourcePermissionStore
+
+The `IResourcePermissionStore` interface is responsible for retrieving resource permission values from the database. This module provides the default implementation that stores permissions in the database.
+
+You can query the store directly if needed:
+
+````csharp
+public class MyService : ITransientDependency
+{
+ private readonly IResourcePermissionStore _resourcePermissionStore;
+
+ public MyService(IResourcePermissionStore resourcePermissionStore)
+ {
+ _resourcePermissionStore = resourcePermissionStore;
+ }
+
+ public async Task GetGrantedResourceKeysAsync(string permissionName)
+ {
+ // Get all resource keys where the permission is granted
+ return await _resourcePermissionStore.GetGrantedResourceKeysAsync(
+ "MyApp.Document",
+ permissionName);
+ }
+}
+````
+
+## Cleaning Up Resource Permissions
+
+When a resource is deleted, you should clean up its associated permissions to avoid orphaned permission records in the database. You can do this directly in your delete logic or handle it asynchronously through event handlers:
+
+````csharp
+public async Task DeleteDocumentAsync(Guid id)
+{
+ // Delete the document
+ await _documentRepository.DeleteAsync(id);
+
+ // Clean up all permissions for this resource
+ await _resourcePermissionManager.DeleteAsync(
+ resourceName: "MyApp.Document",
+ resourceKey: id.ToString(),
+ providerName: "U",
+ providerKey: null // Deletes for all users
+ );
+
+ await _resourcePermissionManager.DeleteAsync(
+ resourceName: "MyApp.Document",
+ resourceKey: id.ToString(),
+ providerName: "R",
+ providerKey: null // Deletes for all roles
+ );
+}
+````
+
+> ABP modules automatically handle permission cleanup for their own entities. For your custom entities, you are responsible for cleaning up resource permissions when resources are deleted.
+
## Permission Management Providers
-Permission Management Module is extensible, just like the [permission system](../framework/fundamentals/authorization.md). You can extend it by defining permission management providers.
+Permission Management Module is extensible, just like the [permission system](../framework/fundamentals/authorization/index.md). You can extend it by defining permission management providers.
[Identity Module](identity.md) defines the following permission management providers:
@@ -111,6 +303,109 @@ Configure(options =>
The order of the providers are important. Providers are executed in the reverse order. That means the `CustomPermissionManagementProvider` is executed first for this example. You can insert your provider in any order in the `Providers` list.
+### Resource Permission Management Providers
+
+Similar to standard permission management providers, you can create custom providers for resource permissions by implementing `IResourcePermissionManagementProvider` or inheriting from `ResourcePermissionManagementProvider`:
+
+````csharp
+public class CustomResourcePermissionManagementProvider
+ : ResourcePermissionManagementProvider
+{
+ public override string Name => "Custom";
+
+ public CustomResourcePermissionManagementProvider(
+ IResourcePermissionGrantRepository resourcePermissionGrantRepository,
+ IGuidGenerator guidGenerator,
+ ICurrentTenant currentTenant)
+ : base(
+ resourcePermissionGrantRepository,
+ guidGenerator,
+ currentTenant)
+ {
+ }
+}
+````
+
+After creating the custom provider, you need to register your provider using the `PermissionManagementOptions` in your module class:
+
+````csharp
+Configure(options =>
+{
+ options.ResourceManagementProviders.Add();
+});
+````
+
+## Permission Value Providers
+
+Permission value providers are used to determine if a permission is granted. They are different from management providers: **value providers** are used when *checking* permissions, while **management providers** are used when *setting* permissions.
+
+> For standard permissions, see the [Authorization document](../framework/fundamentals/authorization/index.md) for details on permission value providers.
+
+### Resource Permission Value Providers
+
+Similar to the standard permission system, you can create custom value providers for resource permissions. ABP comes with two built-in resource permission value providers:
+
+* `UserResourcePermissionValueProvider` (`U`): Checks permissions granted directly to users
+* `RoleResourcePermissionValueProvider` (`R`): Checks permissions granted to roles
+
+You can create your own custom value provider by implementing the `IResourcePermissionValueProvider` interface or inheriting from the `ResourcePermissionValueProvider` base class:
+
+````csharp
+using System.Threading.Tasks;
+using Volo.Abp.Authorization.Permissions.Resources;
+
+public class OwnerResourcePermissionValueProvider : ResourcePermissionValueProvider
+{
+ public override string Name => "Owner";
+
+ public OwnerResourcePermissionValueProvider(
+ IResourcePermissionStore permissionStore)
+ : base(permissionStore)
+ {
+ }
+
+ public override async Task CheckAsync(
+ ResourcePermissionValueCheckContext context)
+ {
+ // Check if the current user is the owner of the resource
+ var currentUserId = context.Principal?.FindUserId();
+ if (currentUserId == null)
+ {
+ return PermissionGrantResult.Undefined;
+ }
+
+ // Your logic to determine if user is the owner
+ var isOwner = await CheckIfUserIsOwnerAsync(
+ currentUserId.Value,
+ context.ResourceName,
+ context.ResourceKey);
+
+ return isOwner
+ ? PermissionGrantResult.Granted
+ : PermissionGrantResult.Undefined;
+ }
+
+ private Task CheckIfUserIsOwnerAsync(
+ Guid userId,
+ string resourceName,
+ string resourceKey)
+ {
+ // Implement your ownership check logic
+ throw new NotImplementedException();
+ }
+}
+````
+
+Register your custom provider in your module's `ConfigureServices` method:
+
+````csharp
+Configure(options =>
+{
+ options.ResourceValueProviders.Add();
+});
+````
+
## See Also
-* [Authorization](../framework/fundamentals/authorization.md)
\ No newline at end of file
+* [Authorization](../framework/fundamentals/authorization/index.md)
+* [Resource-Based Authorization](../framework/fundamentals/authorization/resource-based-authorization.md)
diff --git a/docs/en/release-info/index.md b/docs/en/release-info/index.md
index e3d3aa56cd..b7da966e43 100644
--- a/docs/en/release-info/index.md
+++ b/docs/en/release-info/index.md
@@ -1,16 +1,135 @@
```json
//[doc-seo]
{
- "Description": "Explore the latest ABP Framework release information, including notes, migration guides, and upgrading tips to enhance your development experience."
+ "Description": "Understand ABP Platform's versioning, release schedule, LTS support policy, and how we handle breaking changes to ensure smooth upgrades for your applications."
}
```
-# Release Information
+# Versioning & Releases
-* [Release Notes](./release-notes.md)
-* [Migration Guides](./migration-guides/index.md)
-* [Road Map](./road-map.md)
-* [Upgrading](./upgrading.md)
-* [Preview Releases](./previews.md)
-* [Nightly Releases](./nightly-builds.md)
-* [Official Packages](https://abp.io/packages)
\ No newline at end of file
+ABP Platform follows a predictable release cycle aligned with .NET releases. This document explains our versioning strategy, release schedule, support policy, and how we handle breaking changes.
+
+## Our Commitment
+
+As a framework you build upon, ABP must be both reliable and evolving — stable enough to trust for long-term projects, yet continuously improving with new features and the latest .NET advancements. To achieve this balance, we commit to:
+
+* **Predictable releases**: Major versions annually, aligned with .NET releases
+* **Long-term support**: Every major version receives 2 years of support
+* **Smooth upgrades**: Comprehensive migration guides and tooling for version updates
+* **Transparent communication**: Clear documentation of breaking changes and deprecations
+
+## ABP Versioning
+
+ABP version numbers indicate the level of changes that are introduced by the release. This use of [semantic versioning](https://semver.org/) helps you understand the potential impact of updating to a new version.
+
+ABP version numbers have three parts: `major.minor.patch`. For example, version 10.1.2 indicates major version 10, minor version 1, and patch level 2.
+
+The version number is incremented based on the level of change included in the release.
+
+| Level of change | Details |
+| --- | --- |
+| **Major release** | Contains significant new features. Aligned with the new major .NET release. Some developer assistance is expected. You should check the [migration guide](migration-guides/index.md) and possibly refactor code to adapt to new APIs. |
+| **Minor release** | Contains new features and improvements. Minor releases are generally backward-compatible; minimal developer assistance is expected, but you can optionally modify your applications to begin using new APIs and features. |
+| **Patch release** | Low risk, bug fix and security patch release. No developer assistance is expected. |
+
+> **Note:** ABP version is aligned with .NET version. For example, ABP 10.x runs on .NET 10, ABP 9.x runs on .NET 9.
+
+### Preview Releases
+
+We provide preview releases for each major and minor release so you can try new features before the stable release:
+
+| Pre-release type | Details |
+| --- | --- |
+| **Release Candidate (RC)** | A release that is feature complete and in final testing. RC releases are indicated by a release tag appended with the `-rc` identifier, such as `10.1.0-rc.1`. |
+| **Nightly builds** | The latest development builds published every weekday night. Nightly builds allow you to try the previous day's development. |
+
+See the [Preview Releases](previews.md) and [Nightly Builds](nightly-builds.md) documents for more information.
+
+## Release Frequency
+
+We work toward a regular schedule of releases, so that you can plan and coordinate your updates with the continuing evolution of ABP and the .NET platform.
+
+> **Note:** Dates are offered as general guidance and are subject to change.
+
+In general, expect the following release cycle:
+
+* **A major release once a year**, typically in November, following the new major .NET release
+* **2-4 minor releases** for each major version, released every ~3 months after the major release
+* **Patch releases** as needed, typically every 2-4 weeks for the latest minor version
+
+This cadence of releases gives eager developers access to new features as soon as they are fully developed and tested, while maintaining the stability and reliability of the platform for production users.
+
+### Release Schedule
+
+| Version | Status | Released | Active Ends | LTS Ends |
+| --- | --- | --- | --- | --- |
+| ^10.0.0 | Active | 2025-11 | 2026-11 | 2027-11 |
+| ^9.0.0 | LTS | 2024-11 | 2025-11 | 2026-11 |
+
+
+See the [Release Notes](release-notes.md) for detailed information about each release.
+
+## Support Policy and Schedule (LTS)
+
+ABP Platform follows a **Long-Term Support (LTS)** policy to ensure your applications remain secure and stable over time.
+
+### Support Window
+
+Every major version has a **2-year lifecycle** with two distinct phases:
+
+| Support Stage | Duration | Details |
+| --- | --- | --- |
+| **Active** | ~1 year | The version is under active development. Regularly-scheduled updates and patches are released. New features and improvements are added in minor versions. |
+| **Long-Term Support (LTS)** | ~1 year | Only critical fixes and security patches are released. No new features are added. |
+
+This means we actively develop a major version for about 1 year (until the next major .NET release), then provide LTS support for another year.
+
+### LTS Fixes
+
+As a general rule, a fix is considered for an LTS version if it resolves one of:
+
+* A newly identified security vulnerability
+* A critical bug that significantly impacts production applications
+* A regression caused by a 3rd party change, such as a new browser version or dependency update
+
+## Deprecation Policy
+
+When the ABP team intends to remove an API or feature, it will be marked as *deprecated*. This occurs when an API is obsolete, superseded by another API, or otherwise discontinued. Deprecated APIs remain available through their deprecated phase, which lasts a minimum of one major version (approximately one year).
+
+To help ensure that you have sufficient time and a clear path to update, this is our deprecation policy:
+
+| Deprecation Stage | Details |
+| --- | --- |
+| **Announcement** | We announce deprecated APIs and features in the [release notes](release-notes.md) and [migration guides](migration-guides/index.md). Deprecated APIs are typically marked with `[Obsolete]` attribute in the code, which enables IDEs to provide warnings if your project depends on them. We also announce a recommended update path. |
+| **Deprecation period** | When an API or feature is deprecated, it is still present in at least the next major release. After that, deprecated APIs and features are candidates for removal. A deprecation can be announced in any release, but the removal of a deprecated API or feature happens only in major releases. |
+| **NuGet/NPM dependencies** | We typically make dependency updates that require changes to your applications in major releases. In minor releases, we may update dependencies by expanding the supported versions, but we try not to require projects to update these dependencies until the next major version. |
+
+## Breaking Changes Policy
+
+Breaking changes require you to do work because the state after the change is not backward compatible with the state before it. Examples of breaking changes include the removal of public APIs, changes to method signatures, changing the timing of events, or updating to a new version of a dependency that includes breaking changes itself.
+
+### How We Handle Breaking Changes
+
+To support you in case of breaking changes:
+
+* We follow our [deprecation policy](#deprecation-policy) before we remove a public API
+* We provide detailed [migration guides](migration-guides/index.md) when a version includes breaking changes
+
+### Update Path
+
+We recommend updating one major version at a time for a smoother upgrade experience. For example, to update from version 8.x to version 10.x:
+
+1. Update from version 8.x to version 9.x
+2. Update from version 9.x to version 10.x
+
+See the [upgrading](upgrading.md) document for detailed instructions on how to upgrade your solutions.
+
+## Related Documents
+
+* [Release Notes](release-notes.md) - Detailed release notes for each version
+* [Migration Guides](migration-guides/index.md) - Step-by-step guides for upgrading between versions
+* [Road Map](road-map.md) - Upcoming features and planned releases
+* [Upgrading](upgrading.md) - How to upgrade your ABP-based solutions
+* [Preview Releases](previews.md) - Information about preview/RC releases
+* [Nightly Builds](nightly-builds.md) - How to use nightly builds
+* [Official Packages](https://abp.io/packages) - Browse all ABP packages
diff --git a/docs/en/release-info/migration-guides/abp-10-1.md b/docs/en/release-info/migration-guides/abp-10-1.md
new file mode 100644
index 0000000000..dce45aff64
--- /dev/null
+++ b/docs/en/release-info/migration-guides/abp-10-1.md
@@ -0,0 +1,52 @@
+```json
+//[doc-seo]
+{
+ "Description": "Upgrade your ABP solutions from v10.0 to v10.1 with this comprehensive migration guide, ensuring compatibility and new features with ABP v10.1."
+}
+```
+
+# ABP Version 10.1 Migration Guide
+
+This document is a guide for upgrading ABP v10.0 solutions to ABP v10.1. There are some changes in this version that may affect your applications. Please read them carefully and apply the necessary changes to your application.
+
+## Open-Source (Framework)
+
+### Add New EF Core Migrations for Password History/User Passkey Entities
+
+In this version, we added password history/ user passkeys support to the [Identity PRO Module](../../modules/identity-pro.md) to enhance security compliance. A new `IdentityUserPasswordHistory` entity has been added to store previous password hashes, preventing users from reusing recent passwords. Additionally, we have introduced an `IdentityUserPasskey `entity to support passkey-based authentication.
+
+**You need to create a new EF Core migration and apply it to your database** after upgrading to ABP 10.1.
+
+> See [#23894](https://github.com/abpframework/abp/pull/23894) for more details.
+
+## PRO
+
+> Please check the **Open-Source (Framework)** section before reading this section. The listed topics might affect your application and you might need to take care of them.
+
+If you are a paid-license owner and using the ABP's paid version, then please follow the following sections to get informed about the breaking changes and apply the necessary ones:
+
+### AI Management Module: `Workspace` Entity Base Class Changed
+
+In this version, the `Workspace` entity in the [AI Management Module](../../modules/ai-management/index.md) has been changed from `FullAuditedAggregateRoot` to `AuditedAggregateRoot` as the base class.
+
+**This change removes support for soft deletion and related auditing features. If you are using the AI Management module, you need to create a new EF Core migration and apply it to your database.**
+
+> **Important:** If you have soft-deleted Workspaces in your database, they will become visible after this update. You may need to create a migration script to clean up already deleted records before applying the migration.
+
+### CMS Kit Pro Module: Dynamic FAQ Group Management
+
+In this version, the FAQ group system in the [CMS Kit Pro Module](../../modules/cms-kit-pro/faq.md) has been redesigned to support dynamic group management. FAQ groups are now **first-class entities** stored in the database, replacing the previous static configuration approach.
+
+**Key Changes:**
+
+- A new `FaqGroup` entity has been introduced with unique names for FAQ groups
+- The `FaqSection` entity now uses `GroupId` (Guid) instead of `GroupName` (string) _(GroupName is deprecated and will be removed soon. Use GroupId instead.)_
+- Static FAQ group configuration (`FaqOptions.SetGroups`) has been removed
+
+**Migration Steps:**
+
+1. **Remove static group configuration** from your code (e.g., `Configure(options => { options.SetGroups([...]); })`)
+2. **Create a new EF Core migration and apply it to your database**
+3. **Run the one-time data migration seeder** to migrate existing FAQ sections to the new group entity model
+
+> **Note:** If you have existing FAQ data, you may need to create a data seeder to migrate your existing group associations to the new entity-based model.
diff --git a/docs/en/release-info/migration-guides/index.md b/docs/en/release-info/migration-guides/index.md
index f0d8a78b84..61bb095434 100644
--- a/docs/en/release-info/migration-guides/index.md
+++ b/docs/en/release-info/migration-guides/index.md
@@ -9,6 +9,7 @@
The following documents explain how to migrate your existing ABP applications. We write migration documents only if you need to take an action while upgrading your solution. Otherwise, you can easily upgrade your solution using the [abp update command](../upgrading.md).
+- [10.0 to 10.1](abp-10-1.md)
- [9.x to 10.0](abp-10-0.md)
- [9.2 to 9.3](abp-9-3.md)
- [9.x to 9.2](abp-9-2.md)
diff --git a/docs/en/release-info/release-notes.md b/docs/en/release-info/release-notes.md
index bf335fa7a2..08d0bcde31 100644
--- a/docs/en/release-info/release-notes.md
+++ b/docs/en/release-info/release-notes.md
@@ -14,8 +14,23 @@ Also see the following notes about ABP releases:
* [ABP Studio release notes](../studio/release-notes.md)
* [Change logs for ABP pro packages](https://abp.io/pro-releases)
+## 10.1 (2026-01-06)
+
+> This is currently a RC (release-candidate) and you can see the detailed **[blog post / announcement](https://abp.io/community/announcements/announcing-abp-10-1-release-candidate-cyqui19d)** for the v10.1 release.
+
+* Resource-Based Authorization
+* Introducing the [TickerQ Background Worker Provider](../framework/infrastructure/background-workers/tickerq.md)
+* Angular UI: Version Upgrade to **v21**
+* [File Management Module](../modules/file-management.md): Public File Sharing Support
+* [Payment Module](../modules/payment.md): Public Page Implementation for Blazor & Angular UIs
+* [AI Management Module](../modules/ai-management/index.md) for Blazor & Angular UIs
+* [Identity PRO Module](../modules/identity-pro.md): Password History Support
+* [Account PRO Module](../modules/account-pro.md): Introducing WebAuthn Passkeys
+
## 10.0 (2025-11-18)
+> **Note**: ABP has upgraded to .NET 10.0, so if you plan to use ABP 10.0, you’ll need to migrate your solutions to .NET 10.0. You can refer to the [Migrate from ASP.NET Core 9.0 to 10.0](https://learn.microsoft.com/en-us/aspnet/core/migration/90-to-100) documentation for guidance. However, ABP’s NuGet packages are compatible with both .NET 9 and .NET 10, allowing developers to continue using .NET 9 while still enjoying the latest features and improvements of the ABP Framework without upgrading their SDK.
+
See the detailed **[blog post / announcement](https://abp.io/community/announcements/abp.io-platform-10.0-final-has-been-released-spknn925)** for the v10.0 release.
* Upgraded to .NET 10.0
diff --git a/docs/en/release-info/road-map.md b/docs/en/release-info/road-map.md
index 0df94ec157..70a9b392c4 100644
--- a/docs/en/release-info/road-map.md
+++ b/docs/en/release-info/road-map.md
@@ -1,7 +1,7 @@
```json
//[doc-seo]
{
- "Description": "Explore the ABP Platform Road Map for insights on upcoming features, release schedules, and improvements in version 9.1, launching January 2025."
+ "Description": "Explore the ABP Platform Road Map for insights on upcoming features, release schedules, and improvements in version 10.1, launching January 2026."
}
```
@@ -11,33 +11,35 @@ This document provides a road map, release schedule, and planned features for th
## Next Versions
-### v10.1
+### v10.2
-The next version will be 10.1 and planned to release the stable 10.1 version in January 2026. We will be mostly working on the following topics:
+The next version will be 10.2 and planned to release the stable 10.2 version in April 2026. We will be mostly working on the following topics:
* Framework
- * OpenTelemetry Protocol Support for 3rd-party Integrations
- * Resource Based Authorization Integration
+ * Resource-Based Authorization Improvements
+ * Handle datetime/timezon in `AbpExtensibleDataGrid` Component
* Upgrading 3rd-party Dependencies
* Enhancements in the Core Points
* ABP Suite
- * Define Navigation Properties Without Target String Property Dependency
+ * Creating enums on-the-fly (without needing to create manually on the code side)
+ * Improvements on the generated codes for nullability
* Improvements on Master-Detail Page Desing (making it more compact)
* Improvements One-To-Many Scenarios
* File Upload Modal Enhancements
* ABP Studio
* Allow to Directly Create New Solutions with ABP's RC (Release Candidate) Versions
+ * Integrate AI Management Module with all solution templates and UIs
* Automate More Details on New Service Creation for a Microservice Solution
* Allow to Download ABP Samples from ABP Studio
- * Task Panel Enhancements (and Documentation)
+ * Task Panel Documentation
* Support Multiple Concurrent Kubernetes Deployment/Integration Scenarios
* Improve the Module Installation Experience / Installation Guides
* Application Modules
- * Payment Module: Public Page Implementation (for Blazor & Angular UIs)
- * AI Management Module: UI Implementation for Blazor & Angular UIs
+ * AI Management: MCP & RAG Supports
+ * File Management: Using Resource-Based Permission (on file-sharing and more...)
* CMS Kit: Enhancements for Some Features (Rating, Dynamic Widgets, FAQ and more...)
* UI/UX Improvements on Existing Application Modules
diff --git a/docs/en/release-info/upgrading.md b/docs/en/release-info/upgrading.md
index 88ce1249d8..974d7ef2d5 100644
--- a/docs/en/release-info/upgrading.md
+++ b/docs/en/release-info/upgrading.md
@@ -21,6 +21,8 @@ Run this command in the terminal while you are in the root folder of your soluti
> If your solution has the Angular UI, you probably have `aspnet-core` and `angular` folders in the solution. Run this command in the parent folder of these two folders.
+You can also specify a target version with `--version` parameter. See the [ABP CLI update command](../cli/index.md#update) for all available options.
+
### Database Migrations
> Warning: Be careful if you are migrating your database since you may have data loss in some cases. Carefully check the generated migration code before executing it. It is suggested to take a backup of your current database.
diff --git a/docs/en/solution-templates/layered-web-application/overview.md b/docs/en/solution-templates/layered-web-application/overview.md
index 4c9678fcb2..1a091649cc 100644
--- a/docs/en/solution-templates/layered-web-application/overview.md
+++ b/docs/en/solution-templates/layered-web-application/overview.md
@@ -41,7 +41,7 @@ The following **libraries and services** come **pre-installed** and **configured
The following features are built and pre-configured for you in the solution.
* **Authentication** is fully configured based on best practices.
-* **[Permission](../../framework/fundamentals/authorization.md)** (authorization), **[setting](../../framework/infrastructure/settings.md)**, **[feature](../../framework/infrastructure/features.md)** and the **[localization](../../framework/fundamentals/localization.md)** management systems are pre-configured and ready to use.
+* **[Permission](../../framework/fundamentals/authorization/index.md)** (authorization), **[setting](../../framework/infrastructure/settings.md)**, **[feature](../../framework/infrastructure/features.md)** and the **[localization](../../framework/fundamentals/localization.md)** management systems are pre-configured and ready to use.
* **[Background job system](../../framework/infrastructure/background-jobs/index.md)**.
* **[BLOB storge](../../framework/infrastructure/blob-storing/index.md)** system is installed with the [database provider](../../framework/infrastructure/blob-storing/database.md).
* **On-the-fly database migration** system (services automatically migrated their database schema when you deploy a new version). **\***
diff --git a/docs/en/solution-templates/microservice/overview.md b/docs/en/solution-templates/microservice/overview.md
index 6f0e2e9399..fc322565c3 100644
--- a/docs/en/solution-templates/microservice/overview.md
+++ b/docs/en/solution-templates/microservice/overview.md
@@ -49,7 +49,7 @@ The following features are built and pre-configured for you in the solution.
* **OpenId Connect Authentication**, if you have selected the MVC UI.
* **Authorization code flow** is implemented, if you have selected a SPA UI (Angular or Blazor WASM).
* Other flows (resource owner password, client credentials...) are easy to use when you need them.
-* **[Permission](../../framework/fundamentals/authorization.md)** (authorization), **[setting](../../framework/infrastructure/settings.md)**, **[feature](../../framework/infrastructure/features.md)** and the **[localization](../../framework/fundamentals/localization.md)** management systems are pre-configured and ready to use.
+* **[Permission](../../framework/fundamentals/authorization/index.md)** (authorization), **[setting](../../framework/infrastructure/settings.md)**, **[feature](../../framework/infrastructure/features.md)** and the **[localization](../../framework/fundamentals/localization.md)** management systems are pre-configured and ready to use.
* **[Background job system](../../framework/infrastructure/background-jobs/index.md)** with [RabbitMQ integrated](../../framework/infrastructure/background-jobs/rabbitmq.md).
* **[BLOB storge](../../framework/infrastructure/blob-storing/index.md)** system is installed with the [database provider](../../framework/infrastructure/blob-storing/database.md) and a separate database.
* **On-the-fly database migration** system (services automatically migrated their database schema when you deploy a new version)
diff --git a/docs/en/solution-templates/microservice/permission-management.md b/docs/en/solution-templates/microservice/permission-management.md
index e4e721a2cd..87aa0f8757 100644
--- a/docs/en/solution-templates/microservice/permission-management.md
+++ b/docs/en/solution-templates/microservice/permission-management.md
@@ -27,7 +27,7 @@ Since [Permission Management](../../modules/permission-management.md) is a funda
## Permission Management
-The *Administration* microservice provides a set of APIs to manage permissions. Every microservice [defines](../../framework/fundamentals/authorization.md) its own permissions. When a microservice starts, it registers its permissions to the related permission definition tables if `SaveStaticPermissionsToDatabase` option is true for `PermissionManagementOptions`. Since the default value is true, this behavior is ensured. After that, you can see the permissions from the [Permission Management Dialog](../../modules/permission-management.md#permission-management-dialog) for related provider such as *User*, *Role* or *Client (OpenIddict Applications)*.
+The *Administration* microservice provides a set of APIs to manage permissions. Every microservice [defines](../../framework/fundamentals/authorization/index.md) its own permissions. When a microservice starts, it registers its permissions to the related permission definition tables if `SaveStaticPermissionsToDatabase` option is true for `PermissionManagementOptions`. Since the default value is true, this behavior is ensured. After that, you can see the permissions from the [Permission Management Dialog](../../modules/permission-management.md#permission-management-dialog) for related provider such as *User*, *Role* or *Client (OpenIddict Applications)*.

diff --git a/docs/en/solution-templates/single-layer-web-application/overview.md b/docs/en/solution-templates/single-layer-web-application/overview.md
index a0e1e50093..c7e16090a7 100644
--- a/docs/en/solution-templates/single-layer-web-application/overview.md
+++ b/docs/en/solution-templates/single-layer-web-application/overview.md
@@ -39,7 +39,7 @@ The following **libraries and services** come **pre-installed** and **configured
The solution comes with the following built-in and pre-configured features:
* **Authentication** is fully configured based on best practices.
-* **[Permission](../../framework/fundamentals/authorization.md)** (authorization), **[setting](../../framework/infrastructure/settings.md)**, **[feature](../../framework/infrastructure/features.md)** and the **[localization](../../framework/fundamentals/localization.md)** management systems are pre-configured and ready to use.
+* **[Permission](../../framework/fundamentals/authorization/index.md)** (authorization), **[setting](../../framework/infrastructure/settings.md)**, **[feature](../../framework/infrastructure/features.md)** and the **[localization](../../framework/fundamentals/localization.md)** management systems are pre-configured and ready to use.
* **[Background job system](../../framework/infrastructure/background-jobs/index.md)**.
* **[BLOB storge](../../framework/infrastructure/blob-storing/index.md)** system is installed with the [database provider](../../framework/infrastructure/blob-storing/database.md).
* **On-the-fly database migration** system (services automatically migrated their database schema when you deploy a new version). **\***
diff --git a/docs/en/solution-templates/single-layer-web-application/solution-structure.md b/docs/en/solution-templates/single-layer-web-application/solution-structure.md
index ffb085855f..dd24b91f09 100644
--- a/docs/en/solution-templates/single-layer-web-application/solution-structure.md
+++ b/docs/en/solution-templates/single-layer-web-application/solution-structure.md
@@ -56,7 +56,7 @@ This template uses a single-project structure, with concerns separated into fold
* **Migrations**: Contains the database migration files. It is created automatically by EF Core.
* **ObjectMapping**: Define your [object-to-object mapping](../../framework/infrastructure/object-to-object-mapping.md) classes in this folder.
* **Pages**: Define your UI pages (Razor Pages) in this folder (create `Controllers` and `Views` folders yourself if you prefer the MVC pattern).
-* **Permissions**: Define your [permissions](../../framework/fundamentals/authorization.md) in this folder.
+* **Permissions**: Define your [permissions](../../framework/fundamentals/authorization/index.md) in this folder.
* **Services**: Define your [application services](../../framework/architecture/domain-driven-design/application-services.md) in this folder.
### How to Run?
diff --git a/docs/en/studio/concepts.md b/docs/en/studio/concepts.md
index 1e6afb4fab..1530f3a029 100644
--- a/docs/en/studio/concepts.md
+++ b/docs/en/studio/concepts.md
@@ -43,6 +43,45 @@ An ABP Studio module is a sub-solution that contains zero, one or multiple packa
An ABP Studio Package typically matches to a .NET project (`csproj`).
+### Metadata
+
+Metadata is a collection of key-value pairs that provide additional information for various ABP Studio features. Metadata follows a hierarchical structure where values defined at lower levels override those at higher levels:
+
+**Hierarchy (from highest to lowest priority):**
+1. **Helm Chart Metadata** - Defined in chart properties (Kubernetes context only)
+2. **Kubernetes Profile Metadata** / **Run Profile Metadata** - Defined in profile settings (context-dependent)
+3. **Solution Metadata** - Defined via *Solution Explorer* → right-click solution → *Manage Metadata*
+4. **Global Metadata** - Defined via *Tools* → *Global Metadata*
+
+**Common Metadata Keys:**
+
+| Key | Description | Used By |
+|-----|-------------|---------|
+| `k8ssuffix` | Appends a suffix to Kubernetes namespace (e.g., for multi-developer scenarios) | Kubernetes integration |
+| `dotnetEnvironment` | Specifies the .NET environment (e.g., `Development`, `Staging`) | Helm chart installation |
+| `projectPath` | Path to the project for Docker image building | Docker image build |
+| `imageName` | Docker image name | Docker image build |
+| `projectType` | Project type (`dotnet` or `angular`) | Docker image build |
+
+> Metadata defined in *Global Metadata* is available for all solutions but will not be shared with team members. Metadata defined at *Solution* or *Profile* level will be shared through solution files.
+
+### Secrets
+
+Secrets are key-value pairs designed for storing sensitive information such as passwords, API keys, and connection strings. Unlike metadata, secrets are stored in the local file system and are not included in solution files for security reasons.
+
+**Hierarchy (from highest to lowest priority):**
+1. **Kubernetes Profile Secrets** / **Run Profile Secrets** - Defined in profile settings (context-dependent)
+2. **Solution Secrets** - Defined via *Solution Explorer* → right-click solution → *Manage Secrets*
+3. **Global Secrets** - Defined via *Tools* → *Global Secrets*
+
+**Common Secret Keys:**
+
+| Key | Description | Used By |
+|-----|-------------|---------|
+| `wireGuardPassword` | Password for WireGuard VPN connection to Kubernetes cluster | Kubernetes integration |
+
+> Secrets are stored locally and are not shared with team members by default. Each developer needs to configure their own secrets.
+
## ABP Studio vs .NET Terms
Some ABP Studio terms may seem conflict with .NET and Visual Studio. To make them even more clear, you can use the following table.
diff --git a/docs/en/studio/custom-commands.md b/docs/en/studio/custom-commands.md
new file mode 100644
index 0000000000..af56492f37
--- /dev/null
+++ b/docs/en/studio/custom-commands.md
@@ -0,0 +1,128 @@
+```json
+//[doc-seo]
+{
+ "Description": "Learn how to create and manage custom commands in ABP Studio to automate build, deployment, and other workflows."
+}
+```
+
+# Custom Commands
+
+````json
+//[doc-nav]
+{
+ "Next": {
+ "Name": "Working with ABP Suite",
+ "Path": "studio/working-with-suite"
+ }
+}
+````
+
+Custom commands allow you to define reusable terminal commands that appear in context menus throughout ABP Studio. You can use them to automate repetitive tasks such as building Docker images, installing Helm charts, running deployment scripts, or executing any custom workflow.
+
+> **Note:** This is an advanced feature primarily intended for teams working with Kubernetes deployments or complex build/deployment workflows. If you're developing a standard application without custom DevOps requirements, you may not need this feature.
+
+## Opening the Management Window
+
+To manage custom commands, right-click on the solution root in *Solution Explorer* and select *Manage Custom Commands*.
+
+
+
+The management window displays all defined commands with options to add, edit, or delete them.
+
+## Creating a New Command
+
+Click the *Add New Command* button to open the command editor dialog.
+
+
+
+## Command Properties
+
+| Property | Description |
+|----------|-------------|
+| **Command Name** | A unique identifier for the command (used internally) |
+| **Display Name** | The text shown in context menus |
+| **Terminal Command** | The PowerShell command to execute. Use `&&&` to chain multiple commands |
+| **Working Directory** | Optional. The directory where the command runs (relative to solution path) |
+| **Condition** | Optional. A [Scriban](https://github.com/scriban/scriban/blob/master/doc/language.md) expression that determines when the command is visible |
+| **Require Confirmation** | When enabled, shows a confirmation dialog before execution |
+| **Confirmation Text** | The message shown in the confirmation dialog |
+
+## Trigger Targets
+
+Trigger targets determine where your command appears in context menus. You can select multiple targets for a single command.
+
+| Target | Location |
+|--------|----------|
+| **Helm Charts Root** | *Kubernetes* panel > *Helm* tab > root node |
+| **Helm Main Chart** | *Kubernetes* panel > *Helm* tab > main chart |
+| **Helm Sub Chart** | *Kubernetes* panel > *Helm* tab > sub chart |
+| **Kubernetes Service** | *Kubernetes* panel > *Kubernetes* tab > service |
+| **Solution Runner Root** | *Solution Runner* panel > profile root |
+| **Solution Runner Folder** | *Solution Runner* panel > folder |
+| **Solution Runner Application** | *Solution Runner* panel > application |
+
+## Execution Targets
+
+Execution targets define where the command actually runs. This enables cascading execution:
+
+- When you trigger a command from a **root or parent item**, it can recursively execute on all matching children
+- For example: trigger from *Helm Charts Root* with execution target *Helm Sub Chart* → the command runs on each sub chart
+
+## Template Variables
+
+Commands support [Scriban](https://github.com/scriban/scriban/blob/master/doc/language.md) template syntax for dynamic values. Use `{%{{{variable}}}%}` to insert context-specific data.
+
+### Available Variables by Context
+
+**Helm Charts:**
+
+| Variable | Description |
+|----------|-------------|
+| `profile.name` | Kubernetes profile name |
+| `profile.namespace` | Kubernetes namespace |
+| `chart.name` | Current chart name |
+| `chart.path` | Chart directory path |
+| `metadata.*` | Hierarchical metadata values (e.g., `metadata.imageName`) |
+| `secrets.*` | Secret values (e.g., `secrets.registryPassword`) |
+
+**Kubernetes Service:**
+
+| Variable | Description |
+|----------|-------------|
+| `name` | Service name |
+| `profile.name` | Kubernetes profile name |
+| `profile.namespace` | Kubernetes namespace |
+| `mainChart.name` | Parent main chart name |
+| `chart.name` | Related sub chart name |
+| `chart.metadata.*` | Chart-specific metadata |
+
+**Solution Runner (Root, Folder, Application):**
+
+| Variable | Description |
+|----------|-------------|
+| `profile.name` | Run profile name |
+| `profile.path` | Profile file path |
+| `application.name` | Application name (Application context only) |
+| `application.baseUrl` | Application URL (Application context only) |
+| `folder.name` | Folder name (Folder/Application context) |
+| `metadata.*` | Profile metadata values |
+| `secrets.*` | Profile secret values |
+
+## Example: Build Docker Image
+
+Here's an example command that builds a Docker image for Helm charts:
+
+**Command Properties:**
+- **Command Name:** `buildDockerImage`
+- **Display Name:** `Build Docker Image`
+- **Terminal Command:** `./build-image.ps1 -ProjectPath {%{{{metadata.projectPath}}}%} -ImageName {%{{{metadata.imageName}}}%}`
+- **Working Directory:** `etc/helm`
+- **Trigger Targets:** Helm Charts Root, Helm Main Chart, Helm Sub Chart
+- **Execution Targets:** Helm Main Chart, Helm Sub Chart
+- **Condition:** `{%{{{metadata.projectPath}}}%}`
+
+This command:
+1. Appears in the context menu of Helm charts root and all chart nodes
+2. Executes on main charts and sub charts (cascading from root if triggered there)
+3. Only shows for charts that have `projectPath` metadata defined
+4. Runs the `build-image.ps1` script with dynamic parameters from metadata
diff --git a/docs/en/studio/images/custom-commands/create-edit-command.png b/docs/en/studio/images/custom-commands/create-edit-command.png
new file mode 100644
index 0000000000..7091516a8f
Binary files /dev/null and b/docs/en/studio/images/custom-commands/create-edit-command.png differ
diff --git a/docs/en/studio/images/custom-commands/management-window.png b/docs/en/studio/images/custom-commands/management-window.png
new file mode 100644
index 0000000000..3a24ac8dc4
Binary files /dev/null and b/docs/en/studio/images/custom-commands/management-window.png differ
diff --git a/docs/en/studio/images/monitoring-applications/tools-create.png b/docs/en/studio/images/monitoring-applications/tools-create.png
index ac473d50da..7d7e7c8825 100644
Binary files a/docs/en/studio/images/monitoring-applications/tools-create.png and b/docs/en/studio/images/monitoring-applications/tools-create.png differ
diff --git a/docs/en/studio/images/overview/kubernetes-integration-helm.png b/docs/en/studio/images/overview/kubernetes-integration-helm.png
index dfd68bd0df..6931739948 100644
Binary files a/docs/en/studio/images/overview/kubernetes-integration-helm.png and b/docs/en/studio/images/overview/kubernetes-integration-helm.png differ
diff --git a/docs/en/studio/images/overview/solution-runner.png b/docs/en/studio/images/overview/solution-runner.png
index 956a669ea3..bfe88f607c 100644
Binary files a/docs/en/studio/images/overview/solution-runner.png and b/docs/en/studio/images/overview/solution-runner.png differ
diff --git a/docs/en/studio/images/solution-explorer/solution-explorer.png b/docs/en/studio/images/solution-explorer/solution-explorer.png
index 377cc6a4f3..6b5cc0ad71 100644
Binary files a/docs/en/studio/images/solution-explorer/solution-explorer.png and b/docs/en/studio/images/solution-explorer/solution-explorer.png differ
diff --git a/docs/en/studio/images/solution-runner/add-task-window.png b/docs/en/studio/images/solution-runner/add-task-window.png
new file mode 100644
index 0000000000..2b96f85118
Binary files /dev/null and b/docs/en/studio/images/solution-runner/add-task-window.png differ
diff --git a/docs/en/studio/images/solution-runner/builtin-tasks.png b/docs/en/studio/images/solution-runner/builtin-tasks.png
new file mode 100644
index 0000000000..c988687c1c
Binary files /dev/null and b/docs/en/studio/images/solution-runner/builtin-tasks.png differ
diff --git a/docs/en/studio/images/solution-runner/csharp-application-context-menu-run-connection.png b/docs/en/studio/images/solution-runner/csharp-application-context-menu-run-connection.png
index b85a6df117..e2727570eb 100644
Binary files a/docs/en/studio/images/solution-runner/csharp-application-context-menu-run-connection.png and b/docs/en/studio/images/solution-runner/csharp-application-context-menu-run-connection.png differ
diff --git a/docs/en/studio/images/solution-runner/csharp-application-context-menu.png b/docs/en/studio/images/solution-runner/csharp-application-context-menu.png
index 08b79c0787..3559a17e49 100644
Binary files a/docs/en/studio/images/solution-runner/csharp-application-context-menu.png and b/docs/en/studio/images/solution-runner/csharp-application-context-menu.png differ
diff --git a/docs/en/studio/images/solution-runner/folder-context-menu-add.png b/docs/en/studio/images/solution-runner/folder-context-menu-add.png
index c83a54f5e0..e50e8010c8 100644
Binary files a/docs/en/studio/images/solution-runner/folder-context-menu-add.png and b/docs/en/studio/images/solution-runner/folder-context-menu-add.png differ
diff --git a/docs/en/studio/images/solution-runner/folder-context-menu.png b/docs/en/studio/images/solution-runner/folder-context-menu.png
index 47f56995f0..cdb9de734d 100644
Binary files a/docs/en/studio/images/solution-runner/folder-context-menu.png and b/docs/en/studio/images/solution-runner/folder-context-menu.png differ
diff --git a/docs/en/studio/images/solution-runner/initial-task-properties-behavior.png b/docs/en/studio/images/solution-runner/initial-task-properties-behavior.png
new file mode 100644
index 0000000000..7adb487ffc
Binary files /dev/null and b/docs/en/studio/images/solution-runner/initial-task-properties-behavior.png differ
diff --git a/docs/en/studio/images/solution-runner/profile-root-context-menu-add.png b/docs/en/studio/images/solution-runner/profile-root-context-menu-add.png
index ec26ebcaf0..823bc622a2 100644
Binary files a/docs/en/studio/images/solution-runner/profile-root-context-menu-add.png and b/docs/en/studio/images/solution-runner/profile-root-context-menu-add.png differ
diff --git a/docs/en/studio/images/solution-runner/profile-root-context-menu.png b/docs/en/studio/images/solution-runner/profile-root-context-menu.png
index 355869511c..8616ffca35 100644
Binary files a/docs/en/studio/images/solution-runner/profile-root-context-menu.png and b/docs/en/studio/images/solution-runner/profile-root-context-menu.png differ
diff --git a/docs/en/studio/images/solution-runner/solution-runner-edit.png b/docs/en/studio/images/solution-runner/solution-runner-edit.png
index 99d0bce1d5..9449456d95 100644
Binary files a/docs/en/studio/images/solution-runner/solution-runner-edit.png and b/docs/en/studio/images/solution-runner/solution-runner-edit.png differ
diff --git a/docs/en/studio/images/solution-runner/solution-runner-properties.png b/docs/en/studio/images/solution-runner/solution-runner-properties.png
new file mode 100644
index 0000000000..a6641f892f
Binary files /dev/null and b/docs/en/studio/images/solution-runner/solution-runner-properties.png differ
diff --git a/docs/en/studio/images/solution-runner/solution-runner.png b/docs/en/studio/images/solution-runner/solution-runner.png
index 5a8b123d6d..460d210e0c 100644
Binary files a/docs/en/studio/images/solution-runner/solution-runner.png and b/docs/en/studio/images/solution-runner/solution-runner.png differ
diff --git a/docs/en/studio/images/solution-runner/solutioın-runner-properties.png b/docs/en/studio/images/solution-runner/solutioın-runner-properties.png
deleted file mode 100644
index 9a6505a7d1..0000000000
Binary files a/docs/en/studio/images/solution-runner/solutioın-runner-properties.png and /dev/null differ
diff --git a/docs/en/studio/images/solution-runner/task-context-menu-add.png b/docs/en/studio/images/solution-runner/task-context-menu-add.png
new file mode 100644
index 0000000000..6ae43bf1de
Binary files /dev/null and b/docs/en/studio/images/solution-runner/task-context-menu-add.png differ
diff --git a/docs/en/studio/images/solution-runner/task-context-menu.png b/docs/en/studio/images/solution-runner/task-context-menu.png
new file mode 100644
index 0000000000..7f4f41b2f7
Binary files /dev/null and b/docs/en/studio/images/solution-runner/task-context-menu.png differ
diff --git a/docs/en/studio/images/solution-runner/task-logs-window.png b/docs/en/studio/images/solution-runner/task-logs-window.png
new file mode 100644
index 0000000000..f68b59aa26
Binary files /dev/null and b/docs/en/studio/images/solution-runner/task-logs-window.png differ
diff --git a/docs/en/studio/images/solution-runner/task-panel.png b/docs/en/studio/images/solution-runner/task-panel.png
new file mode 100644
index 0000000000..c988687c1c
Binary files /dev/null and b/docs/en/studio/images/solution-runner/task-panel.png differ
diff --git a/docs/en/studio/images/solution-runner/task-properties.png b/docs/en/studio/images/solution-runner/task-properties.png
new file mode 100644
index 0000000000..e1246c0c31
Binary files /dev/null and b/docs/en/studio/images/solution-runner/task-properties.png differ
diff --git a/docs/en/studio/installation.md b/docs/en/studio/installation.md
index 6f12953f99..2e1d061920 100644
--- a/docs/en/studio/installation.md
+++ b/docs/en/studio/installation.md
@@ -65,3 +65,7 @@ When you see the "New Version Available" window, follow these steps to upgrade A
2. A progress indicator will display the download status.
3. Once the download is complete, a new modal will appear with the "Install and Relaunch" buttons.
4. Click on the "Install and Relaunch" button to complete the installation process.
+
+## Installing a Specific Version
+
+There is no official support for installing an older version of ABP Studio yet. But, if you want to install an older version of ABP Studio, you can use approach explanined here [https://github.com/enisn/AbpDevTools?tab=readme-ov-file#switch-abp-studio-version](https://github.com/enisn/AbpDevTools?tab=readme-ov-file#switch-abp-studio-version)
\ No newline at end of file
diff --git a/docs/en/studio/kubernetes.md b/docs/en/studio/kubernetes.md
index ae23989f6c..eca8d83c5b 100644
--- a/docs/en/studio/kubernetes.md
+++ b/docs/en/studio/kubernetes.md
@@ -11,8 +11,8 @@
//[doc-nav]
{
"Next": {
- "Name": "Working with ABP Suite",
- "Path": "studio/working-with-suite"
+ "Name": "Custom Commands",
+ "Path": "studio/custom-commands"
}
}
````
@@ -100,7 +100,7 @@ It is the root of all subcharts. When you add a new main chart to the root, it i
- `Install Chart(s)`: Installs the selected chart to the current profile.
- `Uninstall Chart(s)`: Uninstalls the selected chart from the current profile.
- `Properties`: It opens the *Chart Properties* window. You can see the chart information in the *Chart Info* tab. In the *Metadata* tab, you can add metadata for the selected main chart. It overrides the metadata in the profile. In the *Kubernetes Services* tab, you can relate a Kubernetes service with the main chart; however, since the main chart usually doesn't create kubernetes service, we can leave it empty.
-- `Refrest Sub Charts`: Refreshes the subcharts of the selected main chart.
+- `Refresh Sub Charts`: Refreshes the subcharts of the selected main chart.
- `Open With`: You can open the selected chart with *Visual Studio Code* or *File Explorer*.
- `Remove`: Removes the selected main chart from the solution.
@@ -137,6 +137,11 @@ While connected, changing the current profile is not possible. Existing applicat

+When connected, you can right-click on a Kubernetes service to see the following context menu options:
+
+- `Browse`: Opens the [browser](./monitoring-applications.md#browse) and navigates to the Kubernetes service URL. This option is only visible if the service has a related Helm chart with matching *Kubernetes Services* regex pattern.
+- `Enable Interception` / `Disable Interception`: Enables or disables traffic interception for the selected service. See the [Intercept a Service](#intercept-a-service) section for more details.
+
When you are connecting to a Kubernetes cluster, it automatically installs the WireGuard VPN to the Kubernetes cluster for a safe connection. You can specify the *wireGuardPassword* in the *Kubernetes Profile* -> *Secrets* tab or at a higher level such as *Solution Secrets* or *Global Secrets*. If you don't provide a password, it generates a random password and stores it in the *Kubernetes Profile* -> *Secrets*. However, if you try to connect to a cluster that already installed WireGuard VPN, then you should give the same password; otherwise, it won't connect. To see the random password, you can click the *eye* icon in the *Kubernetes Profile* -> *Secrets* tab.

@@ -205,46 +210,12 @@ When you connect to a Kubernetes cluster, it uses the selected profile for Kuber
## Advanced Topics
-### Adding a Custom Command
-
-Custom commands can be added to both the *Helm* and *Kubernetes* tabs within the *Kubernetes* panel. For instance, when [redeploy](#redeploy-a-chart) a chart, it involves building the Docker image and reinstalling it. However, if you are working with a different Kubernetes cluster than Docker Desktop, you'll need to push the Docker image to the registry before the installation process. This can be achieved by incorporating a custom command into the *Kubernetes services*. Custom commands can be added to the *Chart Root*, *Main Chart*, and *Subchart* in the *Helm* tab, as well as to the *Service* in the *Kubernetes* tab.
-
-To do that, open the ABP Solution (*.abpsln*) file with *Visual Studio Code* it's a JSON file and you'll see the existing commands in the `commands` section. Before adding a new command, create a powershell script in the `abp-solution-path/etc/helm` folder. For example, we create a `push-image.ps1` script to push the docker image to the registry. Then, add the following command to the `commands` section.
-
-```JSON
- "kubernetesRedeployWithPushImage": {
- "triggerTargets": [
- "KUBERNETES_SERVICE"
- ],
- "executionTargets": [
- "KUBERNETES_SERVICE"
- ],
- "displayName": " Redeploy with Push Image",
- "workingDirectory": "etc/helm",
- "terminalCommand": "./build-image.ps1 -ProjectPath {%{{{chart.metadata.projectPath}}}%} -ImageName {%{{{chart.metadata.imageName}}}%} -ProjectType {%{{{chart.metadata.projectType}}}%} &&& ./push-image.ps1 -ImageName {%{{{chart.metadata.imageName}}}%} &&& ./install.ps1 -ChartName {%{{{mainChart.name}}}%} -Namespace {%{{{profile.namespace}}}%} -ReleaseName {%{{{mainChart.name}}}%}-{%{{{profile.name}}}%} -DotnetEnvironment {%{{{mainChart.metadata.dotnetEnvironment}}}%}",
- "requireConfirmation": "true",
- "confirmationText": "Are you sure to redeploy with push image the related chart '{%{{{chart.name}}}%}' for the service '{%{{{name}}}%}'?",
- "condition": "{%{{{chart != null && chart.metadata.projectPath != null && chart.metadata.imageName != null && chart.metadata.projectType != null}}}%}"
- }
-```
+### Custom Commands
+
+You can add custom commands to context menus in both the *Helm* and *Kubernetes* tabs. This is useful for automating workflows like pushing Docker images to a registry before installation, or running custom deployment scripts.
-Once the command is added, reload the solution from *File* -> *Reload Solution* in the toolbar. After reloading, you will find the *Redeploy with Push Image* command in the context-menu of the service.
+Custom commands can be added to the *Chart Root*, *Main Chart*, and *Sub Chart* in the *Helm* tab, as well as to the *Service* in the *Kubernetes* tab.

-The JSON object has the following properties:
-
-- `triggerTargets`: Specifies the trigger targets for the command. The added command will appear in these targets. You can add one or more trigger targets, accepting values such as *HELM_CHARTS_ROOT*, *HELM_MAIN_CHART*, *HELM_SUB_CHART* and *KUBERNETES_SERVICE*.
-- `executionTargets`: Specifies the execution targets for the command. When executing the command on a root item, it will recursively execute the command for all children. Acceptable values include *HELM_CHARTS_ROOT*, *HELM_MAIN_CHART*, *HELM_SUB_CHART*, and *KUBERNETES_SERVICE*.
-- `displayName`: Specifies the display name of the command.
-- `workingDirectory`: Specifies the working directory of the command. It's relative to the solution path.
-- `terminalCommand`: Specifies the terminal command for the custom command. The `&&&` operator can be used to run multiple commands in the terminal. Utilize the [Scriban](https://github.com/scriban/scriban/blob/master/doc/language.md) syntax to access input data, which varies based on the execution target.
-- `requireConfirmation`: Specifies whether the command requires confirmation message before execution. Acceptable values include *true* and *false*.
-- `confirmationText`: Specifies the confirmation text for the command. Utilize the [Scriban](https://github.com/scriban/scriban/blob/master/doc/language.md) syntax to access input data, which varies based on the execution target.
-- `condition`: Specifies the condition for the command. If the condition returns *false*, it skips the current item and attempts to execute the command for the next item or child item. Utilize the [Scriban](https://github.com/scriban/scriban/blob/master/doc/language.md) syntax to access input data, which varies based on the execution target.
-
-You can use the following variables in the scriban syntax based on the execution target:
- - `HELM_CHARTS_ROOT`: *profile*, *metadata*, *secrets*
- - `HELM_MAIN_CHART`: *profile*, *chart*, *metadata*, *secret*
- - `HELM_SUB_CHART`: *profile*, *chart*, *metadata*, *secret*
- - `KUBERNETES_SERVICE`: *name*, *profile*, *mainChart*, *chart*, *metadata*, *secret*
+For detailed information on creating and managing custom commands, see the [Custom Commands](custom-commands.md) documentation.
diff --git a/docs/en/studio/monitoring-applications.md b/docs/en/studio/monitoring-applications.md
index 75390bf904..04dac9ef13 100644
--- a/docs/en/studio/monitoring-applications.md
+++ b/docs/en/studio/monitoring-applications.md
@@ -27,7 +27,7 @@ If you want to open any of these tabs in separate window, just drag it from the
## Collecting Telemetry Information
-There are two application [types](./running-applications.md#abp-studio-running-applications): C# and CLI. Only C# applications can establish a connection with ABP Studio and transmit telemetry information via the `Volo.Abp.Studio.Client.AspNetCore` package. However, we can view the *Logs* and *Browse* (if there is a *Launch URL*) for both CLI and C# application types. Upon starting C# applications, they attempt to establish a connection with ABP Studio. When connection successful, you should see a chain icon next to the application name in [Solution Runner](./running-applications.md#run-1). Applications can connect the ABP Studio with *Solution Runner* -> *C# Application* -> *Run* -> *Start* or from an outside environment such as debugging with Visual Studio. Additionally, they can establish a connection from a Kubernetes Cluster through the ABP Studio [Kubernetes Integration: Connecting to the Cluster](../get-started/microservice.md#kubernetes-integration-connecting-to-the-cluster).
+There are two application [types](./running-applications.md#applications): C# and CLI. Only C# applications can establish a connection with ABP Studio and transmit telemetry information via the `Volo.Abp.Studio.Client.AspNetCore` package. However, we can view the *Logs* and *Browse* (if there is a *Launch URL*) for both CLI and C# application types. Upon starting C# applications, they attempt to establish a connection with ABP Studio. When connection successful, you should see a chain icon next to the application name in [Solution Runner](./running-applications.md#start--stop--restart). Applications can connect the ABP Studio with *Solution Runner* -> *C# Application* -> *Run* -> *Start* or from an outside environment such as debugging with Visual Studio. Additionally, they can establish a connection from a Kubernetes Cluster through the ABP Studio [Kubernetes Integration: Connecting to the Cluster](../get-started/microservice.md#kubernetes-integration-connecting-to-the-cluster).
You can [configure](../framework/fundamentals/options.md) the `AbpStudioClientOptions` to disable send telemetry information. The package automatically gets the [configuration](../framework/fundamentals/configuration.md) from the `IConfiguration`. So, you can set your configuration inside the `appsettings.json`:
@@ -66,91 +66,318 @@ In this tab, you can view comprehensive overall information. You have the option

-In the data grid, details for each application are displayed. It's possible to sort rows by columns. When selecting a row, you can right-click to access the context menu, offering various actions. This menu allows for opening related tabs that are filtered by the selected application.
+In the data grid, details for each application are displayed. It's possible to sort rows by columns.
- `Name`: The name of the application.
-- `State`: The state of the application. It can take on several values such as *Scheduled*, *Starting*, *Started*, *Stopping* and *Stopped*. In the event of an application crash during its starting, the state is mark as *Scheduled*, we can cancel the starting process at that stage.
-- `Health` : The health state of the application. Clicking on the icon shows the latest health check response. Displays `N/A` if the application is not running or health check is not configured for the application.
-- `Instances`: Indicates the count of running instances for the application. This value is particularly helpful when scaling the application within a Kubernetes, providing visibility into the number of currently active instances.
+- `State`: The state of the application. It can take on several values such as *Scheduled*, *Starting*, *Started*, *Stopping* and *Stopped*. The *Scheduled* state indicates the application is waiting for an automatic restart (e.g., after a crash or when watch mode detects changes). You can cancel the scheduled restart at this stage.
+- `Health`: The health state of the application. The icon indicates the current health status: *Healthy* (green), *Unhealthy* (red), *Degraded* (yellow), or *Unknown* (gray). Clicking on the icon shows the latest health check response in JSON format. Displays `N/A` if the application is not running or health check is not configured for the application.
+- `Instances`: Indicates the count of running instances for the application. This value is particularly helpful when scaling the application within a Kubernetes cluster, providing visibility into the number of currently active pods.
- `Uptime`: The time elapsed since the application started.
- `Requests`: The number of HTTP requests received by the application.
-- `Events (R/S)`: The number of [Distributed Event](../framework/infrastructure/event-bus/distributed) received or sent by the application.
+- `Events (R/S)`: The number of [Distributed Events](../framework/infrastructure/event-bus/distributed) received or sent by the application.
- `Exceptions`: The number of exceptions thrown by the application.
- `Actions`: The actions that can be performed on the application. You can start and stop the application.
-> For the events system, you can exclusively view the [Distributed Events](../framework/infrastructure/event-bus/distributed). Generally, the [Local Events](../framework/infrastructure/event-bus/distributed) is not included.
+### Context Menu Actions
+
+When selecting a row, you can right-click to access the context menu with the following actions:
+
+| Action | Description |
+|--------|-------------|
+| **Start / Stop** | Start or stop the selected application. |
+| **Restart** | Restart the application (available when the application is running). |
+| **Build** | Build options including *Build*, *Graph Build*, *Restore*, and *Clean* (available for C# applications when stopped). |
+| **Browse** | Open the application in the [Browse](#browse) tab (available when a Launch URL is configured). |
+| **Health Status** | Submenu with *Browse Health UI* and *Show Latest Health Check Response* options. |
+| **Requests** | Open the [HTTP Requests](#http-requests) tab filtered by this application. |
+| **Exceptions** | Open the [Exceptions](#exceptions) tab filtered by this application. |
+| **Logs** | Open the [Logs](#logs) tab with this application selected. |
+| **Copy Url** | Copy the application's URL to the clipboard. |
+| **Properties** | Open the application properties dialog. |
+
+> For the events system, you can exclusively view the [Distributed Events](../framework/infrastructure/event-bus/distributed). Generally, the [Local Events](../framework/infrastructure/event-bus/local) are not included.
## Browse
-ABP Studio includes a browser tool that allows access to websites and running applications. You can open new tabs to browse different websites or view active applications. It's a convenient utility to access websites and applications without leaving ABP Studio. Clicking the *Browse* tab displays the running applications and an *Open new tab* button.
+ABP Studio includes a built-in browser that allows access to websites and running applications. You can open new tabs to browse different websites or view active applications. It's a convenient utility to access websites and applications without leaving ABP Studio. Clicking the *Browse* tab displays the running applications and an *Open new tab* button.

-You can open the *Browse* tabs as many times as you want. It's possible to open the same application in several tabs simultaneously. To open an application, navigate through *Solution Runner* -> *C# or CLI Application* -> *Browse*. This option is only visible when there is a [Launch URL](./running-applications.md#properties). Additionally, you can access any URL by entering it into the address bar.
+You can open the *Browse* tabs as many times as you want. It's possible to open the same application in several tabs simultaneously. To open an application, you can:
+
+- Double-click on an application in the *Solution Runner* tree.
+- Right-click on an application and select *Browse* from the context menu.
+- Click on a running application in the application list shown in the *Browse* tab.
+
+These options are only available when the application has a [Launch URL](./running-applications.md#properties) configured. Additionally, you can access any URL by entering it into the address bar.

-When you click the *Dev Tools* button it opens the [Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools) for the selected tab.
+### Browser Toolbar
+
+The browser toolbar provides the following controls:
+
+| Control | Description |
+|---------|-------------|
+| **Back / Forward** | Navigate through your browsing history within the tab. |
+| **Refresh** | Reload the current page. |
+| **Address Bar** | Enter any URL to navigate directly. The address bar shows the current URL and allows you to navigate to any website. |
+| **Dev Tools** | Opens the [Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools) for the selected tab, allowing you to inspect elements, debug JavaScript, and analyze network requests. |
+| **Clear Cookies** | Clears all cookies for the currently selected tab, useful for testing authentication flows or resetting session state. |
+| **Open in External Browser** | Opens the current URL in your system's default web browser. |

+### Default Credentials Notification
+
+When browsing certain applications (such as AppHost dashboards), ABP Studio displays a notification bar with default credentials. This helps you quickly log in during development. You can dismiss this notification permanently by clicking the *Don't show again* option.
+
## HTTP Requests
Within this tab, you can view all *HTTP Requests* received by your C# applications. You have the option to filter requests based on URLs by using the search textbox or by selecting a particular application from the combobox. The *Clear Requests* button removes all received requests. Moreover, you have the ability to sort requests by columns.

-Clicking on a row enables you to view the details of each HTTP request; `URL`, `Method`, `Status Code`, `Timestamp`, `Headers (Request, Response)`, `Request (Payload)` and `Response`.
+### Request List Columns
+
+The request list displays the following information:
+
+| Column | Description |
+|--------|-------------|
+| **Timestamp** | When the request was received (displayed as HH:mm:ss). |
+| **Application** | The name of the application that received the request. |
+| **Path** | The request URL path and query string. |
+| **Method** | The HTTP method (GET, POST, PUT, DELETE, etc.). |
+| **Status Code** | The HTTP response status code. |
+| **Duration** | The time taken to process the request in milliseconds. |
+| **Request Body Size** | The size of the request payload. |
+| **Response Body Size** | The size of the response payload. |
+
+### Request Details
+
+Clicking on a row enables you to view the details of each HTTP request:
+
+- **URL**: The full request URL.
+- **Method**: The HTTP method used.
+- **Status Code**: The response status code.
+- **Duration**: Request processing time in milliseconds.
+- **Timestamp**: When the request was received.
+- **Headers (Request)**: All request headers sent by the client.
+- **Headers (Response)**: All response headers returned by the server.
+- **Request (Payload)**: The request body with size information.
+- **Response**: The response body with size information.

-You can format the JSON content by clicking the *Format* button.
+### Formatting JSON Content
+
+Both request and response payloads have a *Format* button that formats JSON content for better readability. This is available when the content type is `application/json`.

-Furthermore, by clicking the gear icon in the *HTTP Requests* tab, you can access the *Solution Runner HTTP Requests Options* window. Within the *Ignored URLs* tab, you have the ability to exclude particular URLs by applying a regex pattern. Excluded URLs won't be visible in the *HTTP Requests* tab. By default, the metrics URL is already ignored. You can add or remove items as needed.
+### Quick URL Filter
+
+When viewing request details, you can right-click and select *Filter Selected URL* to quickly apply the current URL as a filter. This is useful when you want to see all requests to a specific endpoint.
+
+### Configuring Ignored URLs
+
+By clicking the gear icon in the *HTTP Requests* tab, you can access the *Solution Runner HTTP Requests Options* window. Within the *Ignored URLs* tab, you have the ability to exclude particular URLs by applying a regex pattern. Excluded URLs won't be visible in the *HTTP Requests* tab. By default, the metrics URL is already ignored. You can add or remove items as needed.

-> After adding a new URL, it will only affect subsequent requests.
+> After adding a new URL pattern, it will only affect subsequent requests. Existing requests will remain visible.
## Events
-In this tab, you can view all [Distributed Events](../framework/infrastructure/event-bus/distributed) sent or received by your C# applications. You can filter them by [Event Name](../framework/infrastructure/event-bus/distributed#event-name) using the search textbox or by selecting a specific application. Additionally, you can choose the *Direction* (Received/Send) and *Source* (Direct/Inbox/Outbox) of events. The *Clear Events* button removes all events.
+In this tab, you can view all [Distributed Events](../framework/infrastructure/event-bus/distributed) sent or received by your C# applications. You can filter them by [Event Name](../framework/infrastructure/event-bus/distributed#event-name) using the search textbox or by selecting a specific application. Additionally, you can choose the *Direction* (Received/Sent) and *Source* (Direct/Inbox/Outbox) of events. The *Clear Events* button removes all events.

-> In the *Direction* section, there are two options: *Received*, indicating events received by the application, and *Sent*, indicating events sent by the application. Within the *Source* section, three options are available, and their significance comes into play when utilizing the [Inbox/Outbox pattern](../framework/infrastructure/event-bus/distributed#outbox-inbox-for-transactional-events). *Inbox* refers to events received by the application, *Outbox* refers to events sent by the application and *Direct* signifies events sent or received by the application without involving Inbox/Outbox pattern.
+### Direction and Source Filters
+
+| Filter | Options | Description |
+|--------|---------|-------------|
+| **Direction** | *Received*, *Sent* | Filter by whether events were received or sent by the application. |
+| **Source** | *Direct*, *Inbox*, *Outbox* | Filter by event source. Relevant when using the [Inbox/Outbox pattern](../framework/infrastructure/event-bus/distributed#outbox-inbox-for-transactional-events). |
+
+- **Direct**: Events sent or received without using the Inbox/Outbox pattern.
+- **Inbox**: Events received through the transactional inbox.
+- **Outbox**: Events sent through the transactional outbox.
-Clicking on a row enables you to view the details of each event; `Application`, `Event Name`, `Direction`, `Source`, `Timestamp` and `Event Data`.
+### Event Details
+
+Clicking on a row enables you to view the details of each event:
+
+- **Application**: The application that sent or received the event.
+- **Event Name**: The full event type name.
+- **Direction**: Whether the event was received or sent.
+- **Source**: The event source (Direct, Inbox, or Outbox).
+- **Timestamp**: When the event was processed.
+- **Event Data**: The event payload in JSON format.

+### Formatting Event Data
+
+The *Event Data* section includes a *Format* button that formats the JSON content for better readability. This makes it easier to inspect complex event payloads.
+
+> ABP Studio automatically decodes Base64-encoded event data. If your event bus uses Base64 encoding for message transport, the data will be displayed in its decoded, readable form.
+
## Exceptions
-This tab displays all exceptions by your C# applications. You can apply filters using the search textbox based on *Message*, *Source*, *ExceptionType*, and *StackTrace* or by choosing a specific application. Additionally, you have the option to select the [Log Level](../framework/fundamentals/exception-handling.md#log-level) for adding a filter. To clear all exceptions, use the *Clear Exceptions* button.
+This tab displays all exceptions thrown by your C# applications. You can apply filters using the search textbox based on *Message*, *Source*, *ExceptionType*, and *StackTrace* or by choosing a specific application. Additionally, you have the option to select the [Log Level](../framework/fundamentals/exception-handling.md#log-level) for filtering. To clear all exceptions, use the *Clear Exceptions* button.

-Click on a row to inspect the details of each exception; `Application`, `Exception Type`, `Source`, `Timestamp`, `Level`, `Message` and `StackTrace`.
+### Exception List
+
+The exception list shows a summary of each exception:
+
+- **Exception Type**: The short exception type name (e.g., `NullReferenceException` instead of `System.NullReferenceException`).
+- **Message**: A truncated version of the exception message.
+- **Timestamp**: When the exception occurred.
+- **Level**: The log level (Error, Warning, etc.).
+
+### Exception Details
+
+Click on a row to inspect the full details of each exception:
+
+- **Application**: The application where the exception occurred.
+- **Exception Type**: The full exception type name including namespace.
+- **Source**: The source file or component where the exception originated.
+- **Timestamp**: When the exception was thrown.
+- **Level**: The log level associated with this exception.
+- **Message**: The complete exception message.
+- **StackTrace**: The full stack trace showing the call hierarchy.

+### Inner Exceptions
+
+If an exception contains inner exceptions, they are displayed hierarchically in the details panel. This allows you to trace the root cause of wrapped exceptions, which is common in scenarios like database errors wrapped in application-level exceptions.
+
## Logs
-The *Logs* tab allows you to view all logs for both CLI and C# applications. To access logs, simply select an application. You can also apply filters using the search textbox by log text or by selecting a specific *Log Level*. When you select a *Log Level* it shows selected log level and higher log levels. For example, if you select *Warning* it shows *Warning*, *Error* and *Critical* logs. To clear selected application logs, use the *Clear Logs* button. If *Auto Scroll* is checked, the display automatically scrolls when new logs are received.
+The *Logs* tab allows you to view all logs for both CLI and C# applications. To access logs, simply select an application from the dropdown. You can also apply filters using the search textbox by log text or by selecting a specific *Log Level*.

+### Log Level Filtering
+
+When you select a *Log Level*, it shows the selected level and all higher severity levels:
+
+| Selected Level | Shows |
+|---------------|-------|
+| **Trace** | Trace, Debug, Information, Warning, Error, Critical |
+| **Debug** | Debug, Information, Warning, Error, Critical |
+| **Information** | Information, Warning, Error, Critical |
+| **Warning** | Warning, Error, Critical |
+| **Error** | Error, Critical |
+| **Critical** | Critical only |
+| **None** | All logs (no filtering) |
+
+### Log Display Features
+
+- **Color Coding**: Log entries are color-coded by level for quick visual identification. Errors and critical logs stand out with distinct colors.
+- **Auto Scroll**: When enabled, the display automatically scrolls to show new logs as they arrive. This is useful for real-time monitoring.
+- **Clear Logs**: Clears the logs for the currently selected application only. Other applications' logs remain intact.
+- **Text Filter**: Search within log messages using the search textbox. The filter is applied in real-time with a slight delay for performance.
+
## Tools
-The *Tools* tab allows you to easily access to the user interfaces of the tools you are using. A *tool* may be related with a docker container, or independent. If it is related with a container (ex: *grafana*), the tool is opened when the container is up. If the tool is independent, it will be always opened.
+The *Tools* tab provides quick access to web-based management interfaces for infrastructure services like Grafana, RabbitMQ, pgAdmin, and Redis Commander. Each tool opens in a dedicated browser tab within ABP Studio, eliminating the need to switch between external browser windows.

-The microservice template comes with pre-defined tools to display related container user interfaces. You can edit existing tools, add new tools or delete existing tools.
+The microservice template includes pre-configured tools for common infrastructure services. You can customize these tools or add new ones based on your project requirements.
-In the example below, a new tool named `My Application Status` will be added to the tools and it will display the URL in the input:
+### Adding a New Tool
+
+To add a new tool, click the *+* button in the *Tools* tab. This opens the *Create Tool* dialog where you can configure the tool properties.

+### Tool Properties
+
+Each tool has the following configurable properties:
+
+| Property | Required | Description |
+|----------|----------|-------------|
+| **Name** | Yes | A unique identifier displayed as the tab header. |
+| **URL** | Yes | The web interface URL (e.g., `http://localhost:3000`). |
+| **Related Container** | No | Docker container name. When set, the tool activates only when this container is running. |
+| **Related Kubernetes Service** | No | A regex pattern to match Kubernetes service names for automatic URL switching. |
+| **Related Kubernetes Service Port** | No | The port to use when connecting via Kubernetes service. |
+
+### Editing and Removing Tools
+
+- **Edit**: Right-click on a tool tab and select *Edit* to modify its properties.
+- **Remove**: Right-click on a tool tab and select *Close* to remove it from the profile.
+- **Clear Cookies**: Right-click on a tool tab and select *Clear Cookies* to reset the browser session for that tool.
+
+### Tool Activation States
+
+Tools can be in different activation states depending on their configuration:
+
+| State | Condition | Behavior |
+|-------|-----------|----------|
+| **Always Active** | No *Related Container* specified | Tool is always accessible regardless of container state. |
+| **Container-Dependent** | *Related Container* specified | Tool activates only when the specified Docker container is running. |
+| **Kubernetes-Aware** | *Related Kubernetes Service* specified | Tool URL switches between local and Kubernetes endpoints automatically. |
+
+### Kubernetes Integration
+
+When you specify a *Related Kubernetes Service*, the tool gains the ability to seamlessly switch between local and Kubernetes environments. This is particularly useful for microservice development where you run some services locally while others remain in a Kubernetes cluster.
+
+**Automatic URL Switching:**
+
+1. **Local Mode**: When the *Related Container* is running, the tool uses the configured *URL* (e.g., `http://localhost:3000`).
+2. **Kubernetes Mode**: When the container stops and you're [connected to a Kubernetes cluster](./kubernetes.md#connecting-to-a-kubernetes-cluster), the tool automatically redirects to the matching Kubernetes service.
+3. **Pattern Matching**: The *Related Kubernetes Service* accepts regex patterns. For example, `.*-grafana` matches any service name ending with `-grafana`.
+
+> This automatic switching eliminates the need to manually update URLs when transitioning between local development and Kubernetes-based testing.
+
+### Run Profile Configuration
+
+Tools are persisted in the Run Profile file (`.abprun.json`). Below is an example configuration with common infrastructure tools:
+
+```json
+{
+ "tools": {
+ "grafana": {
+ "url": "http://localhost:3000",
+ "relatedContainer": "grafana",
+ "relatedKubernetesService": ".*-grafana",
+ "relatedKubernetesServicePort": 3000
+ },
+ "rabbitmq": {
+ "url": "http://localhost:15672",
+ "relatedContainer": "rabbitmq",
+ "relatedKubernetesService": ".*-rabbitmq",
+ "relatedKubernetesServicePort": 15672
+ },
+ "redis-commander": {
+ "url": "http://localhost:8081",
+ "relatedContainer": "redis-commander"
+ },
+ "pgadmin": {
+ "url": "http://localhost:5050",
+ "relatedContainer": "pgadmin"
+ },
+ "seq": {
+ "url": "http://localhost:5341"
+ }
+ }
+}
+```
+
+### Default Credentials
+
+Some tools display a notification bar with default credentials when opened for the first time:
+
+| Tool | Username | Password |
+|------|----------|----------|
+| Grafana | `admin` | `admin` |
+| RabbitMQ | `guest` | `guest` |
+
+> You can dismiss this notification permanently by clicking the *Don't show again* option.
diff --git a/docs/en/studio/overview.md b/docs/en/studio/overview.md
index 3f5835c998..9ad50ad95a 100644
--- a/docs/en/studio/overview.md
+++ b/docs/en/studio/overview.md
@@ -91,13 +91,13 @@ Kubernetes integration in ABP Studio enables users to deploy solutions directly
This pane is dedicated to managing [Helm](https://helm.sh/) charts, which are packages used in Kubernetes deployments. It simplifies the process of building images and installing charts.
-
+
#### Kubernetes
This pane is dedicated to managing Kubernetes services. It simplifies the process of redeploying and intercepting application service.
-
+
## Application Monitoring Area
diff --git a/docs/en/studio/release-notes.md b/docs/en/studio/release-notes.md
index d2eba88e0f..13e206127e 100644
--- a/docs/en/studio/release-notes.md
+++ b/docs/en/studio/release-notes.md
@@ -9,7 +9,14 @@
This document contains **brief release notes** for each ABP Studio release. Release notes only include **major features** and **visible enhancements**. Therefore, they don't include all the development done in the related version.
-## 2.1.3 (2025-12-15) Latest
+## 2.1.4 (2025-12-30) Latest
+
+* Fixed books sample for blazor-webapp tiered solution.
+* Fixed K8s cluster deployment issues for microservices.
+* Fixed docker build problem on microservice template.
+* Showed logs of the executed tasks.
+
+## 2.1.3 (2025-12-15)
* Updated `createCommand` and CLI help for multi-tenancy.
* Fixed `BookController` templating problem.
diff --git a/docs/en/studio/running-applications.md b/docs/en/studio/running-applications.md
index c922c1b501..a46e137db4 100644
--- a/docs/en/studio/running-applications.md
+++ b/docs/en/studio/running-applications.md
@@ -1,11 +1,11 @@
```json
//[doc-seo]
{
- "Description": "Learn how to use the ABP Studio's Solution Runner to efficiently run applications and organize projects with customizable profiles."
+ "Description": "Learn how to use the ABP Studio's Solution Runner to run applications, manage tasks, and organize projects with customizable profiles."
}
```
-# ABP Studio: Running Applications
+# ABP Studio: Solution Runner
````json
//[doc-nav]
@@ -17,19 +17,16 @@
}
````
-Use the *Solution Runner* to easily run your application(s) and set up infrastructure. You can create different profiles to organize projects based on your needs and teams. Simply navigate to the *Solution Runner* panel in the left menu.
+Use the *Solution Runner* to easily run your application(s), execute tasks, and set up infrastructure. You can create different profiles to organize projects based on your needs and teams. Simply navigate to the *Solution Runner* panel in the left menu.

-> The project structure might be different based on your selection. For example MVC microservice project, looks like the following. You can edit the tree structure as you wish.
+The Solution Runner contains two tabs:
-The solution runner contains 4 different types to define tree structure.
+- **Applications**: For managing and running your applications.
+- **Tasks**: For managing tasks that can be executed on demand or automatically when the solution is opened.
-- **Profile**: We can create different profiles to manage the tree as our needs. For example we can create 2 different profile for `team-1` and `team-2`. `team-1` want to see the only *Administration* and *Identity* service, `team-2` see the *Saas* and *AuditLogging* services. With that way each team see the only services they need to run. In this example `Default` profile *Acme.BookStore (Default)* comes out of the box when we create the project.
-- **Folder**: We can organize the applications with *Folder* type. In this example, we keep services in `services` folder for our microservice projects. We can also use nested folder if we want `apps`, `gateways`` and `services` is the folders in current(`Default`) profile.
-- **C# Application**: We can add any C# application from our [Solution Explorer](./solution-explorer.md). If the application is not in our solution, we can add it externally by providing the *.csproj* file path. The .NET icon indicates that the application is a C# project. For example, `Acme.BookStore.AuthServer`, `Acme.BookStore.Web`, `Acme.BookStore.WebGateway`, etc., are C# applications.
-- **CLI Application**: We can add [powershell](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core) commands to prepare some environments or run other application types than C# such as angular.
-- **Docker Container**: We can add Docker container files to control them on UI, start/stop containers individually.
+> The project structure might be different based on your selection. For example MVC microservice project, looks like the following. You can edit the tree structure as you wish.
## Profile
@@ -47,13 +44,23 @@ When you click *Add New Profile*, it opens the *Create New Profile* window. You
> When a profile is edited or deleted while running some applications, those applications will be stopped. However, applications running under a different profile will continue to run unaffected. Lastly, if we add a new profile, all applications running under existing profiles will be stopped.
-## Using the Profile
+## Applications
+
+The **Applications tab** allows you to manage and run your applications. The solution runner contains 4 different types to define tree structure:
+
+- **Profile**: We can create different profiles to manage the tree as our needs. For example we can create 2 different profile for `team-1` and `team-2`. `team-1` want to see the only *Administration* and *Identity* service, `team-2` see the *Saas* and *AuditLogging* services. With that way each team see the only services they need to run. In this example `Default` profile *Acme.BookStore (Default)* comes out of the box when we create the project.
+- **Folder**: We can organize the applications with *Folder* type. In this example, we keep services in `services` folder for our microservice projects. We can also use nested folder if we want `apps`, `gateways` and `services` are the folders in current(`Default`) profile.
+- **C# Application**: We can add any C# application from our [Solution Explorer](./solution-explorer.md). If the application is not in our solution, we can add it externally by providing the *.csproj* file path. The .NET icon indicates that the application is a C# project. For example, `Acme.BookStore.AuthServer`, `Acme.BookStore.Web`, `Acme.BookStore.WebGateway`, etc., are C# applications.
+- **CLI Application**: We can add [powershell](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core) commands to prepare some environments or run other application types than C# such as angular.
+- **Docker Container**: We can add Docker container files to control them on UI, start/stop containers individually.
+
+### Using the Profile
After selecting the current profile, which is the *Default* profile that comes pre-configured, we can utilize the tree items. This allows us to execute collective commands and create various tree structures based on our specific needs. You can navigate through the root of the tree and right-click to view the context menu, which includes the following options: `Start All`, `Stop All`, `Build`, `Add`, and `Manage Start Actions`.

-### Start/Stop All
+#### Start/Stop All
We can start/stop the applications with these options. Go to the root of the tree and right-click to view the context menu:
@@ -62,7 +69,7 @@ We can start/stop the applications with these options. Go to the root of the tre
> You can change the current profile while applications are running in the previous profile. The applications continue to run under the previous profile. For example, if we start the `Acme.BookStore.AdministrationService`, `Acme.BookStore.IdentityService` applications when the current profile is *team-1* and after changing the current profile to *team-2* the applications continue to run under *team-1*.
-### Build
+#### Build
We can use common [dotnet](https://learn.microsoft.com/en-us/dotnet/core/tools) commands in this option. Go to the root of the tree and right-click to view the context menu, in this example *Acme.Bookstore(Default)* -> *Build*, there are 4 options available:
@@ -75,13 +82,15 @@ We can use common [dotnet](https://learn.microsoft.com/en-us/dotnet/core/tools)
> Since *Solution Runner* may contain numerous C# projects, the *Build* options uses the [Background Tasks](./overview#background-tasks), ensuring a seamless experience while using ABP Studio.
-### Add
+#### Add
+
+We can add 4 different item types to *Profile* for defining the tree structure. Those options are `C# Application`, `CLI Application`, `Docker Container`, and `Folder`.
-We can add 3 different item type to *Profile* for defining the tree structure. Those options are `C# Application`, `CLI Application` and `Folder`.
+> Note: The `Docker Container` option is only available when right-clicking the profile root. When right-clicking a folder, only `C# Application`, `CLI Application`, and `Folder` options are available.

-#### C# Application
+##### C# Application
When we go to the root of the tree and right-click, in this example *Acme.BookStore(Default)* -> *Add* -> *C# Application* it opens the *Add Application* window. There are two methods to add applications: *This solution* and *External*. To add via the *This solution* tab, follow these steps:
@@ -98,7 +107,6 @@ The C# project doesn't have to be within the current [Solution Explorer](./solut

-
- `Path`: Provide the path to the .csproj file you wish to add. The path will be [normalized](https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#path-normalization), allowing the project location to be flexible, as long as it's accessible from the current [ABP Solution](./concepts.md#solution).
- `Name`: Give an arbitrary name to see in solution runner. This name should be unique for each profile.
- `Launch url`: This is the url when we want to browse. But if the added project doesn't have launch url we can leave it empty.
@@ -106,7 +114,7 @@ The C# project doesn't have to be within the current [Solution Explorer](./solut
You can click the `OK` button to add the C# application to the profile.
-#### CLI Application
+##### CLI Application
We can add any [powershell](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core) file to execute from the solution runner. With this flexibility we can prepare our infrastructure environment such as `Docker-Dependencies` or run different application types like `Angular`. You can add CLI applications with root of the tree and right-click, in this example *Acme.BookStore(Default)* -> *Add* -> *CLI Application*.
@@ -122,7 +130,7 @@ We can add any [powershell](https://learn.microsoft.com/en-us/powershell/module/
You can click the `OK` button to add the CLI application to the profile.
-#### Folder
+##### Folder
When adding applications directly to the root of the tree, it can become disorganized, especially with numerous projects. Utilizing a folder structure allows us to organize applications more efficiently. This method enables executing collective commands within a specified folder. When we go to root of the tree and right-click, in this example *Acme.BookStore(Default)* -> *Add* -> *Folder* it opens *New folder* window.
@@ -132,7 +140,7 @@ When adding applications directly to the root of the tree, it can become disorga
You can click the `OK` button to add the folder to the profile.
-### Miscellaneous
+#### Miscellaneous
- You can drag and drop folder and application into folder for organization purposes. Click and hold an item, then drag it into the desired folder.
- We can start all applications by clicking the *Play* icon on the left side, similar way we can stop all applications by clicking the *Stop* icon on the left side.
@@ -140,7 +148,7 @@ You can click the `OK` button to add the folder to the profile.
- To remove a folder from the tree, open the context menu by right-clicking the folder and selecting *Delete*.
- When starting applications, they continue to restart until the application starts gracefully. To stop the restarting process when attempting to restart the application, click the icon on the left. Additionally, you can review the *Logs* to understand why the application isn't starting gracefully.
-### Manage Start Actions
+#### Manage Start Actions
This command will open a dialog where you can set start actions and start orders of sub-applications and sub-folders.
@@ -152,83 +160,107 @@ You can order the applications by dragging the icon in the first column. In the
- **Build**: This option allows to disable/enable build before starting the application. If you are working on a single application, you can exclude the other applications from build to save time. This option can also be set by performing `right click > properties` on applications.
- **Watch**: When enabled, changes in your code are watched and dotnet hot-reloads the application or restarts it if needed. This option also can be set by performing `right click > properties` on applications.
-## Folder
+### Folder Context Menu
-We already now why we need folder in the [previous](./running-applications.md#folder) section, we can use collective commands within this folder items. To do that go to folder and open the context menu by right-clicking, which includes 5 options `Start`, `Stop`, `Build`, `Add`, `Manage Start Actions`, `Rename` and `Delete`.
+We already now why we need folder in the [previous](#folder) section, we can use collective commands within this folder items. To do that go to folder and open the context menu by right-clicking, which includes 5 options `Start`, `Stop`, `Build`, `Add`, `Manage Start Actions`, `Rename` and `Delete`.

-### Start/Stop
+#### Start/Stop
You can see the context menu by right-clicking *Folder*. It will start/stop all the applications under the folder.
-### Build
+#### Build
*Folder* -> *Build* context menu, it's the [similar](#build) options like *Acme.BookStore(Default)* -> *Builds* options there are 4 options available. The only difference between them it's gonna be execute in selected folder.

-### Add
+#### Add
*Folder* -> *Add* context menu, it's the [same](#add) options like *Acme.BookStore(Default)* -> *Add* there are 3 options avaiable. The only difference, it's gonna add item to the selected folder.

-### Miscellaneous
+#### Miscellaneous
- You can rename a folder with *Folder* -> *Rename*.
- You can delete a folder with *Folder* -> *Delete*.
-## C# Application
+### C# Application
-The .NET icon indicates that the application is a C# project. After we [add](#c-application) the C# applications to the root of the tree or folder, we can go to any C# application and right-click to view the context menu; `Start`, `Build`, `Browse`, `Requests`, `Exceptions`, `Logs`, `Copy URL`, `Properties`, `Remove`.
+The .NET icon indicates that the application is a C# project. After we [add](#c-application) the C# applications to the root of the tree or folder, we can go to any C# application and right-click to view the context menu; `Start`, `Stop`, `Restart`, `Build`, `Browse`, `Health Status`, `Requests`, `Exceptions`, `Logs`, `Copy URL`, `Properties`, `Remove`, and `Open with`.

-### Start
+#### Start / Stop / Restart
-Starts the selected application. Once it is started, *Stop* and *Restart* options will be available.
+- **Start**: Starts the selected application.
+- **Stop**: Stops the running application.
+- **Restart**: Restarts the running application. This option is only available when the application is started.
> When you start the C# application, you should see a *chain* icon next to the application name, that means the started application connected to ABP Studio. C# applications can connect to ABP Studio even when running from outside the ABP Studio environment, for example debugging with Visual Studio. If the application is run from outside the ABP Studio environment, it will display *(external)* information next to the chain icon.
-### Build
+#### Build
It's the [similar](#build) options like root of the tree options. The only difference between them it's gonna be execute the selected application.

-### Monitoring
+#### Monitoring
-When the C# application is connected to ABP Studio, it starts sending telemetry information to see in one place. We can easily click these options to see the detail; `Browse`, `Requests`, `Exceptions` and `Logs`.
+When the C# application is connected to ABP Studio, it starts sending telemetry information to see in one place. We can easily click these options to see the detail; `Browse`, `Health Status`, `Requests`, `Exceptions`, `Events` and `Logs`.

- `Browse`: ABP Studio includes a browser tool for accessing websites and running applications. You can click this option to view the application in the ABP Studio browser. However, this option is only accessible if the application is started.
-- Health Status : If Health Check endpoints are defined, it allows you to browse Health UI and see the latest health check response.
+- `Health Status`: A submenu that provides health monitoring options when Health Check endpoints are defined:
+ - **Browse Health UI**: Opens the Health UI page of the application in the built-in browser.
+ - **Show Latest Health Check Response**: Displays the latest health check response in JSON format.
- `Requests`: It opens the *HTTP Requests* tab with adding the selected application filter. You can view all *HTTP Requests* received by your applications.
- `Exceptions`: We can display all exceptions on this tab. It opens the *Exceptions* tab with selected application.
+- `Events`: Opens the *Events* tab filtered by this application. You can view all [Distributed Events](../framework/infrastructure/event-bus/distributed) sent or received by this application.
- `Logs`: Clicking this option opens the *Logs* tab with adding the selected application filter.
-### Properties
+#### Properties
We can open the *Application Properties* window to change *Launch url*, *Health check endpoints*, *Kubernetes service* and *run* information. To access the *Application Properties* window, navigate to a C# application, right-click to view the context menu, and select the Properties option.
-
+
-- **Health check endpoint**: Endpoint for controlling the health status of the application periodically. If the application doesn't have a endpoint for health check, you can enter `/` to use the home page of the application as health check endpoint.
+- **Launch URL**: The URL used when browsing the application. This is the address where the application is accessible.
+- **Kubernetes service**: A regex pattern to match Kubernetes service names. When connected to a Kubernetes cluster, ABP Studio uses this pattern to find the corresponding Kubernetes service and uses its URL instead of the Launch URL. This applies to *Browse*, *Copy URL*, and *Health UI* features. For example, if your Helm chart creates a service named `bookstore-identity-service`, you can use `.*-identity-service` or `bookstore-identity.*` as the pattern. For [microservice](../get-started/microservice.md) templates, this is pre-configured.
+- **Health check endpoint**: Endpoint for controlling the health status of the application periodically. If the application doesn't have an endpoint for health check, you can enter `/` to use the home page of the application as health check endpoint.
- **Health UI endpoint**: Endpoint of the Health UI page of the application.
-- **Skip build before starting**: When enabled, application is started without build and it makes starting faster. This is useful when you are working on a single application out of multiple, so you don't need to build others everytime they start.
-- **Watch changes while running**: When enabled, you should see an *eye* icon next to the application name.
+- **Skip build before starting**: When enabled, the application is started without building, making startup faster. This is useful when you are working on a single application out of multiple, so you don't need to build others every time they start.
+- **Watch changes while running**: When enabled, changes in your code are watched and dotnet hot-reloads the application or restarts it if needed. You should see an *eye* icon next to the application name when this is enabled.
+- **Open browser on start**: When enabled, the application is automatically opened in the Browse tab when it starts.
+- **Auto refresh browser on restart**: When enabled, browser tabs showing this application are automatically refreshed when the application restarts.
+- **Runnable**: Controls whether the application can be started from Solution Runner. When disabled, the application will not be included in batch start operations.

+#### Open with
+
+The *Open with* submenu provides options to open the application project in external tools:
+
+- **Visual Studio**: Opens the project in Visual Studio (available if installed).
+- **Visual Studio Code**: Opens the project folder in Visual Studio Code (available if installed).
+- **JetBrains Rider**: Opens the project in JetBrains Rider (available if installed).
+- **Terminal**: Opens a terminal window in the project directory.
+- **Explorer / Finder**: Opens the project folder in the system file explorer.
+
+### Custom Commands
+
+You can add custom commands that appear in the context menu of Solution Runner items (root, folders, and applications). These commands allow you to automate custom workflows and scripts. For details on creating and managing custom commands, see the [Custom Commands](custom-commands.md) documentation.
+
### Miscellaneous
-- We can copy the selected application *Browse* URL with *Copy URL*. It copies the *Browse* URL instead *Launch URL* since we could connected to *Kubernetes* service.
+- We can copy the selected application *Browse* URL with *Copy URL*. It copies the *Browse* URL instead of *Launch URL* since we could be connected to a *Kubernetes* service.
- You can change the target framework by right-click the selected application and change the *Target Framework* option. This option visible if the project has multiple target framework such as MAUI applications.
- To remove an application from the tree, open the context menu by right-clicking the application and selecting *Remove*.
-## CLI Application
+### CLI Application
CLI applications uses the [powershell](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core) commands. With this way we can start and stop anything we want. After we add the CLI applications to root of the tree or folder, we can go to any CLI application and right-click to view the context menu.
@@ -242,7 +274,7 @@ CLI applications uses the [powershell](https://learn.microsoft.com/en-us/powersh
> When CLI applications start chain icon won't be visible, because only C# applications can connect the ABP Studio.
-## Docker Containers
+### Docker Containers
Each Docker container represents a `.yml` file. Each file can be run on UI individually. A file may contain one or more services. To start/stop each service individually, we recommend to keep services in separate files.
@@ -294,9 +326,7 @@ If the `yml` file contains multiple services, they will be represented as a sing
>
> 
-
-
-### Properties
+#### Properties

@@ -304,10 +334,160 @@ In properties dialog, you can set the name of docker compose stack name of the c

-## Docker Compose
+### 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.
\ No newline at end of file
+> Note: The **Docker Compose** is not available in the ABP Studio interface.
+
+## Tasks
+
+The **Tasks tab** in the Solution Runner allows you to define and execute tasks for your solution. You can run initialization tasks after solution creation, create reusable tasks that can be executed on demand, and optionally configure tasks to run automatically when a solution is opened. Navigate to the *Solution Runner* panel in the left menu and select the *Tasks* tab.
+
+
+
+> The task structure might vary based on your solution template. ABP Studio solution templates come with pre-configured tasks like *Initialize Solution*.
+
+### Tasks Panel Overview
+
+The Tasks panel has a tree structure similar to the Applications panel:
+
+- **Root**: The root item displays your solution name, similar to the Solution Explorer.
+- **Tasks**: Individual tasks that can be started, stopped, and configured.
+
+Tasks are associated with the current *Profile*, just like applications. When you switch profiles, the task configuration may differ based on what's defined in each profile.
+
+### Built-in Tasks
+
+When you create a new solution using ABP Studio templates, the following tasks are automatically configured in the *Default* profile:
+
+
+
+#### Initialize Solution
+
+The *Initialize Solution* task performs the initial setup required after creating a new solution or cloning an existing one from source control. This task is configured with the *Run on solution initialization (1 time per computer)* behaviour, meaning it runs automatically only once per computer when the solution is initialized.
+
+
+
+The initialization typically includes:
+
+- Installing NPM packages (`abp install-libs`)
+- Creating development certificates (for OpenIddict)
+- and more... (depends on solution type)
+
+> This task is designed to be idempotent, meaning it can be run multiple times without causing issues.
+
+#### Migrate Database
+
+The *Migrate Database* task runs the database migration for your solution. This is useful when you need to apply new migrations after pulling changes from source control or when you've added new migrations yourself.
+
+> Unlike the *Initialize Solution* task, the *Migrate Database* task is not configured to run automatically on solution open by default.
+
+### Adding a Task
+
+To add a new task, right-click on the root item (solution name) in the Tasks panel and select *AddTask* item.
+
+
+
+This opens the *Add Task* window where you can configure the task:
+
+
+
+- **Name**: Provide a unique name for the task. This name will be displayed in the Tasks panel tree.
+- **Working Directory**: Specify the directory where the task will be executed. You can use the folder picker to browse and select the directory. The path will be [normalized](https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#path-normalization), allowing flexibility in the folder location.
+- **Start Command**: Provide the script or command to execute when the task starts. Use the local path prefix `./` if the script is in the working directory (e.g., `./scripts/my-task.ps1`). You can also pass arguments like `./my-script.ps1 -param value`.
+- **Task Behaviour**: Select how the task should run: *Manual*, *Run on solution open*, or *Run on solution initialization (1 per computer)*.
+- **Short Description**: Short description for the task.
+
+Click *OK* to add the task to the profile.
+
+### Managing Tasks
+
+Once tasks are added to the panel, you can manage them using the context menu. Right-click on a task to see the available options:
+
+
+
+#### Start / Stop
+
+- **Start**: Executes the task's start command. The task status will change to indicate it's running.
+- **Stop**: Stops a running task. If a stop command is configured, it will be executed; otherwise, the process will be terminated.
+
+> Unlike applications, tasks do not automatically restart if they fail or stop. A task can complete its execution and stop itself (like running a database migration), or it can continue running until manually stopped.
+
+#### Logs
+
+Click *Logs* to view the task's output in a dedicated window. This shows the console output from the task's execution, which is helpful for debugging or monitoring progress.
+
+
+
+#### Properties
+
+Click *Properties* to open the task configuration window where you can modify the task settings:
+
+
+
+- **Name**: The display name of the task.
+- **Working Directory**: The execution directory for the task.
+- **Start Command**: The command to run when starting the task.
+- **Task Behaviour**: Select how the task should run: *Manual*, *Run on solution open*, or *Run on solution initialization (1 per computer)*.
+- **Short Description**: Short description for the task.
+
+#### Remove
+
+Select *Remove* to delete the task from the profile. This action only removes the task configuration; it does not delete any script files.
+
+### Run On Solution Open
+
+Tasks configured with *Run on solution open* behaviour are automatically executed every time you open the solution in ABP Studio. This is useful for:
+
+- **Environment checks**: Verifying that required services or dependencies are available.
+- **Recurring setup**: Tasks that need to run on each session, such as starting background services or refreshing configurations.
+
+When the solution is opened:
+
+1. ABP Studio loads the solution and profiles
+2. Tasks marked with *Run on solution open* are queued for execution
+3. Tasks execute in the background, and you can monitor their progress in the [Background Tasks](./overview.md#background-tasks) panel
+
+> Tasks configured with this behaviour should be idempotent, meaning running them multiple times should not cause errors or duplicate operations.
+
+### Run On Solution Initialization (1 Per Computer)
+
+Tasks configured with *Run on solution initialization (1 per computer)* behaviour are executed only once per computer when the solution is first opened. After the initial execution, the task will not run automatically on subsequent solution opens. This is ideal for:
+
+- **One-time setup**: Tasks like *Initialize Solution* that install dependencies, run database migrations, or create development certificates.
+- **First-time configuration**: Setting up environment-specific configurations that only need to happen once.
+
+This behaviour is particularly useful for:
+
+1. **New team members**: When a developer clones the repository and opens the solution for the first time, all initialization tasks run automatically.
+2. **Fresh clones**: Ensures the solution is properly configured without requiring manual intervention.
+3. **Avoiding redundant operations**: Prevents running time-consuming setup tasks on every solution open.
+
+> The *Initialize Solution* task that comes with ABP Studio templates uses this behaviour by default. It checks if NPM packages are already installed before running `abp install-libs` and performs other initialization steps only when necessary.
+
+### Task Configuration in Profiles
+
+Tasks are stored in the run profile JSON files (`*.abprun.json`). The task configuration looks like this:
+
+```json
+{
+ "tasks": {
+ "Initialize Solution": {
+ "behaviour": 2,
+ "startCommand": "./initialize-solution.ps1",
+ "workingDirectory": "../../scripts",
+ "shortDescription": "Installs required UI libraries and creates required certificates."
+ }
+ }
+}
+```
+
+You can manually edit these files if needed, but it's recommended to use the ABP Studio UI for managing tasks to ensure proper formatting and validation.
+
+## See Also
+
+- [Solution Explorer](./solution-explorer.md)
+- [Monitoring Applications](./monitoring-applications.md)
diff --git a/docs/en/studio/solution-explorer.md b/docs/en/studio/solution-explorer.md
index 21dde505e5..05a7f69403 100644
--- a/docs/en/studio/solution-explorer.md
+++ b/docs/en/studio/solution-explorer.md
@@ -11,7 +11,7 @@
//[doc-nav]
{
"Next": {
- "Name": "Running Applications",
+ "Name": "Solution Runner",
"Path": "studio/running-applications"
}
}
@@ -33,6 +33,9 @@ It is the main solution that you can open with ABP Studio, an ABP solution can c
- `New Folder`: Creates a new folder within the ABP Solution, allowing you to organize your modules.
- `New Module`: Allows you to create a new module.
- `Existing Module`: You can add existing module to your solution.
+- `Analyze`: Analyzes all modules and packages in the solution to extract information like aggregate roots, application services, permissions, etc.
+ - `Analyze`: Analyzes all modules and packages using existing build outputs.
+ - `Build & Analyze`: Builds the solution first, then analyzes all modules and packages.
- `Rename`: Renames the solution.
- `Manage Secrets`: You can edit your solution secrets.
- `Manage Metadata`: You can edit your solution metadata.
@@ -52,6 +55,9 @@ It is the main solution that you can open with ABP Studio, an ABP solution can c
- `Clean`: Cleans the output of the previous build for modules.
- `Restore`: Restores the dependencies for modules.
- `Open With`
+ - `Visual Studio`: Opens the solution in Visual Studio. This option is only available if you have Visual Studio installed.
+ - `Visual Studio Code`: Opens the solution in Visual Studio Code. This option is only available if you have Visual Studio Code installed.
+ - `JetBrains Rider`: Opens the solution in JetBrains Rider. This option is only available if you have JetBrains Rider installed.
- `Terminal`: Opens the terminal in the solution directory.
- `Explorer`: Opens the file explorer in the solution directory.
- `Solution Configuration`: You can see the project creation options in this menu. It opens the *Solution Configuration* window.
@@ -73,6 +79,9 @@ You can click the *OK* button to add the folder to the solution. When you right-
- `New Folder`: Creates a new nested folder within the selected folder, allowing you to organize your modules.
- `New Module`: Allows you to create a new module to selected folder.
- `Existing Module`: You can add existing module to your selected folder.
+- `Analyze`: Analyzes all modules and packages in the selected folder.
+ - `Analyze`: Analyzes all modules and packages using existing build outputs.
+ - `Build & Analyze`: Builds first, then analyzes all modules and packages in the folder.
- `Rename`: Renames the selected folder.
- `Delete`: Deletes the selected folder and all child items from the solution.
- `ABP CLI`
@@ -102,6 +111,9 @@ A [module](./concepts.md#module) is a sub-solution that can contains zero, one o
- `New Package`: Creates a new package within the selected module.
- `Existing Package`: You can add existing package to your selected module.
- `Folder`: Creates a new folder within the selected module, allowing you to organize your packages.
+- `Analyze`: Analyzes all packages in the selected module.
+ - `Analyze`: Analyzes all packages using existing build outputs.
+ - `Build & Analyze`: Builds first, then analyzes all packages in the module.
- `Import Module`: This option allows you to import an existing module from *Solution*, *Local*, or *NuGet* into your selected module.
- `Rename`: Renames the selected module.
- `Remove`: Removes the selected module and all child items from the solution.
@@ -126,6 +138,7 @@ A [module](./concepts.md#module) is a sub-solution that can contains zero, one o
- `JetBrains Rider`: Opens the module in JetBrains Rider. This option is only available if you have JetBrains Rider installed.
- `Terminal`: Opens the terminal in the module directory.
- `Explorer`: Opens the file explorer in the module directory.
+- `Open Readme`: Opens the README file in the module if available. This option is only visible if the module has a README file.
- `Upgrade to Pro`: This will be visible only when you purchased a license but still using the modules came with open-source (free) license. For more details, check out [Migrating from Open Source Templates](../guides/migrating-from-open-source.md) document. This is not shown in the screenshot above.
### Adding a New Empty Module
@@ -238,10 +251,18 @@ A [package](./concepts.md#package) is a project that can be added to a module, a

-- `Add Package Reference`: This option allows you to add a package reference to the selected package.
+- `Add`: This menu is only visible for Host projects (except Blazor WebAssembly).
+ - `NPM Package`: Adds an NPM package reference to the selected package.
+- `Add Package Reference`: This option allows you to add a package reference to the selected package. This option is only visible for non-Host projects.
+- `Analyze`: Analyzes the selected package to extract information like aggregate roots, application services, permissions, etc. This menu is only visible for analyzable package types.
+ - `Analyze`: Analyzes the package using existing build outputs.
+ - `Build & Analyze`: Builds first, then analyzes the package.
- `Reload`: Reloads the selected package.
- `Remove`: Removes the selected package from the module.
- `ABP CLI`
+ - `Generate Proxy`: Generates client-side proxy code for the selected package.
+ - `Javascript`: Generates JavaScript proxy code.
+ - `C#`: Generates C# proxy code.
- `Install Libs`: Install NPM Packages for UI projects in your selected package.
- `Upgrade ABP Packages`: Update all the ABP related NuGet and NPM packages in your selected package.
- `Switch to`: It switches your selected package to the specified version of the ABP.
@@ -264,6 +285,7 @@ A [package](./concepts.md#package) is a project that can be added to a module, a
- `JetBrains Rider`: Opens the package in JetBrains Rider. This option is only available if you have JetBrains Rider installed.
- `Terminal`: Opens the terminal in the package directory.
- `Explorer`: Opens the file explorer in the package directory.
+- `Open Readme`: Opens the README file in the package if available. This option is only visible if the package has a README file.
### Adding a New Package
diff --git a/docs/en/studio/working-with-suite.md b/docs/en/studio/working-with-suite.md
index b436b7b0f8..2dc3a4bcc0 100644
--- a/docs/en/studio/working-with-suite.md
+++ b/docs/en/studio/working-with-suite.md
@@ -39,7 +39,10 @@ If There are more than one module which is openable via Suite, Studio will ask y
### Opening from context menu
-Alternatively, you can right click to a module in [Solution Explorer](solution-explorer.md) and click `ABP Suite` to open it with Suite. Suite will automatically open `Crud Page Generation` screen with the selected module.
+You can right-click on the solution root or a module in [Solution Explorer](solution-explorer.md) and click `ABP Suite` to open it with Suite.
+
+- **Solution root**: If you right-click on the solution root and select `ABP Suite`, Studio will ask you to pick a module if there are multiple modules. Suite will then open with the selected module.
+- **Module**: If you right-click on a specific module and select `ABP Suite`, Suite will automatically open `Crud Page Generation` screen with that module.

@@ -51,10 +54,14 @@ Standard application solutions (`App` & `App-nolayers`) generated by Studio are
You can generate code on the services of the microservice solution. UI code generation is not supported at the moment. It is on the roadmap.
-## Managing Installed Version
+## Managing Installed Version
You can update or downgrade the version of `ABP Suite` by `Change Version` button in `ABP Suite` menu.
+### Automatic Version Matching
+
+When you open ABP Suite for a solution, Studio checks if the installed Suite version matches the solution's ABP version. If they differ, Studio will prompt you to update or downgrade Suite to match your solution's version. This ensures compatibility between Suite and your project.
+


diff --git a/docs/en/suite/generating-crud-page.md b/docs/en/suite/generating-crud-page.md
index afc2536709..160f89216f 100644
--- a/docs/en/suite/generating-crud-page.md
+++ b/docs/en/suite/generating-crud-page.md
@@ -270,6 +270,29 @@ In the example above, the `IdentityUser` entity is selected as the navigation pr
> **Note:** Ensure that your solution is built properly before establishing relationship between your own entity and a module entity because ABP Suite scans assemblies and finds which ABP modules you are using and lists their entities in the navigation property model if you have checked the **Include entities from ABP modules** checkbox.
+#### Extending with Custom Module Entities
+
+If you want to extend ABP Suite's system to list entities from your own custom modules (not just ABP's built-in modules), you can configure the `module-entity-extension.json` file. This file is located in the `.suite` folder at the root of your solution (`/.suite/module-entity-extension.json`).
+
+Here is the default sample file content:
+
+```json
+{
+ "Modules": [
+ {
+ "DomainProjectDllFileName": "MySampleModule.MyProject.Domain.dll"
+ }
+ ]
+}
+```
+
+By defining the `DomainProjectDllFileName` property, ABP Suite will scan the specified module's **.dll** and list its entities in the navigation property model. This allows you to create navigation properties that reference entities from your custom modules.
+
+> **Important:** When extending with custom module entities, ensure that:
+> - Your current solution properly depends on the related module.
+> - All module references are correctly configured.
+> - The solution is built successfully before attempting to establish relationships.
+
#### Adding An Existing Entity as a Navigation Property
Alternatively, you can add `IdentityUser` entity (or any other entity) as a navigation property to an entity by manually entering the required information. See the screenshot below:
diff --git a/docs/en/testing/integration-tests.md b/docs/en/testing/integration-tests.md
index c87d270747..d28ed5ca5b 100644
--- a/docs/en/testing/integration-tests.md
+++ b/docs/en/testing/integration-tests.md
@@ -34,13 +34,13 @@ The startup template is configured to use **in-memory SQLite** database for the
Using in-memory SQLite database has two main advantages;
* It is faster compared to an external DBMS.
-* It create a **new fresh database** for each test case, so tests doesn't affect each other.
+* It creates a **new fresh database** for each test case, so tests don't affect each other.
> **Tip**: Do not use EF Core's In-Memory database for advanced integration tests. It is not a real DBMS and has many differences in details. For example, it doesn't support transaction and rollback scenarios, so you can't truly test the failing scenarios. On the other hand, In-Memory SQLite is a real DBMS and supports the fundamental SQL database features.
## The Seed Data
-Writing tests against an empty database is not practical. In most cases, you need to some initial data in the database. For example, if you write a test class that query, update and delete the products, it would be helpful to have a few products in the database before executing the test case.
+Writing tests against an empty database is not practical. In most cases, you need some initial data in the database. For example, if you write a test class that queries, updates and deletes the products, it would be helpful to have a few products in the database before executing the test case.
ABP's [Data Seeding](../framework/infrastructure/data-seeding.md) system is a powerful way to seed the initial data. The application startup template has a *YourProject*TestDataSeedContributor class in the `.TestBase` project. You can fill it to have an initial data that you can use for each test method.
@@ -401,7 +401,7 @@ public class EfCoreIssueAppService_Tests : IssueAppService_Tests By deriving from the related abstract classes, now we can see the all tests in the test explorers and run them.
+> By deriving from the related abstract classes, now we can see all the tests in the test explorers and run them.

diff --git a/docs/en/testing/overall.md b/docs/en/testing/overall.md
index 154c169d5b..15e9abf725 100644
--- a/docs/en/testing/overall.md
+++ b/docs/en/testing/overall.md
@@ -69,7 +69,7 @@ The startup solution has the following libraries already installed;
* [NSubstitute](https://nsubstitute.github.io/) as the mocking library.
* [Shouldly](https://github.com/shouldly/shouldly) as the assertion library.
-While you are free to replace them with your favorite tools, this document and examples will be base on these tooling.
+While you are free to replace them with your favorite tools, this document and examples will be based on these tooling.
## The Test Explorer
diff --git a/docs/en/testing/unit-tests.md b/docs/en/testing/unit-tests.md
index cfc63a4848..2e06a44efb 100644
--- a/docs/en/testing/unit-tests.md
+++ b/docs/en/testing/unit-tests.md
@@ -73,7 +73,7 @@ namespace MyProject.Issues
Notice that the `IsClosed` and `CloseDate` properties have private setters to force some business rules by using the `Open()` and `Close()` methods:
-* Whenever you close an issue, the `CloseDate` should be set to the [current time](../framework/infrastructure/virtual-file-system.md).
+* Whenever you close an issue, the `CloseDate` should be set to the current time.
* An issue can not be re-opened if it is locked. And if it is re-opened, the `CloseDate` should be set to `null`.
Since the `Issue` entity is a part of the Domain Layer, we should test it in the `Domain.Tests` project. Create an `Issue_Tests` class inside the `Domain.Tests` project:
@@ -160,7 +160,7 @@ public void Should_Not_Allow_To_ReOpen_A_Locked_Issue()
`Assert.Throws` checks if the executed code throws a matching exception.
-> See the [xUnit](https://xunit.net/#documentation) & [Shoudly](https://docs.shouldly.org/) documentation to learn more about these libraries.
+> See the [xUnit](https://xunit.net/#documentation) & [Shouldly](https://docs.shouldly.org/) documentation to learn more about these libraries.
## Classes With Dependencies
diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-opening.png b/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-opening.png
index adebe653b5..94dfb63a56 100644
Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-opening.png and b/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-opening.png differ
diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-solution-test-projects-mongo.png b/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-solution-test-projects-mongo.png
index a0ecbd469d..e3032dfbde 100644
Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-solution-test-projects-mongo.png and b/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-solution-test-projects-mongo.png differ
diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-angular.png b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-angular.png
index 687dca3593..5c4a377b4a 100644
Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-angular.png and b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-angular.png differ
diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-blazor.png b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-blazor.png
index 5535659b45..e690e9b0e9 100644
Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-blazor.png and b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-blazor.png differ
diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mauiblazor.png b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mauiblazor.png
index 1bf3253fe7..961e7565a7 100644
Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mauiblazor.png and b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mauiblazor.png differ
diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mvc.png b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mvc.png
index 54860a5e9f..7a2850afc2 100644
Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mvc.png and b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mvc.png differ
diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/studio-browser-suite.png b/docs/en/tutorials/book-store-with-abp-suite/images/studio-browser-suite.png
index 47a37bbd9b..bf1394a0a2 100644
Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/studio-browser-suite.png and b/docs/en/tutorials/book-store-with-abp-suite/images/studio-browser-suite.png differ
diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-1.png b/docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-1.png
index dbb1890c14..b1e4a7d06e 100644
Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-1.png and b/docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-1.png differ
diff --git a/docs/en/tutorials/book-store-with-abp-suite/part-02.md b/docs/en/tutorials/book-store-with-abp-suite/part-02.md
index 11725201cb..f9462b9130 100644
--- a/docs/en/tutorials/book-store-with-abp-suite/part-02.md
+++ b/docs/en/tutorials/book-store-with-abp-suite/part-02.md
@@ -96,7 +96,7 @@ Here is the all details for the `Book` entity:
* `Name` is **required**, it's a **string** property and maximum length is **128**.
* `Type` is an **enum** and the enum file path is *\Acme.BookStore.Domain.Shared\Books\BookType.cs*.
* `PublishDate` is a **DateTime** property and **not nullable**.
-* `Price` is a **float** property and **required**.
+* `Price` is a **float** property.
You can leave the other configurations as default.
diff --git a/docs/en/tutorials/book-store/part-05.md b/docs/en/tutorials/book-store/part-05.md
index 3db00ea423..7449b4b65d 100644
--- a/docs/en/tutorials/book-store/part-05.md
+++ b/docs/en/tutorials/book-store/part-05.md
@@ -30,7 +30,7 @@
## Permissions
-ABP provides an [authorization system](../../framework/fundamentals/authorization.md) based on the ASP.NET Core's [authorization infrastructure](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction). One major feature added on top of the standard authorization infrastructure is the **permission system** which allows to define permissions and enable/disable per role, user or client.
+ABP provides an [authorization system](../../framework/fundamentals/authorization/index.md) based on the ASP.NET Core's [authorization infrastructure](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction). One major feature added on top of the standard authorization infrastructure is the **permission system** which allows to define permissions and enable/disable per role, user or client.
### Permission Names
diff --git a/docs/en/tutorials/book-store/part-08.md b/docs/en/tutorials/book-store/part-08.md
index 68796b99eb..02a0182fe5 100644
--- a/docs/en/tutorials/book-store/part-08.md
+++ b/docs/en/tutorials/book-store/part-08.md
@@ -183,7 +183,7 @@ public class AuthorAppService : BookStoreAppService, IAuthorAppService
}
````
-* `[Authorize(BookStorePermissions.Authors.Default)]` is a declarative way to check a permission (policy) to authorize the current user. See the [authorization document](../../framework/fundamentals/authorization.md) for more. `BookStorePermissions` class will be updated below, don't worry for the compile error for now.
+* `[Authorize(BookStorePermissions.Authors.Default)]` is a declarative way to check a permission (policy) to authorize the current user. See the [authorization document](../../framework/fundamentals/authorization/index.md) for more. `BookStorePermissions` class will be updated below, don't worry for the compile error for now.
* Derived from the `BookStoreAppService`, which is a simple base class comes with the startup template. It is derived from the standard `ApplicationService` class.
* Implemented the `IAuthorAppService` which was defined above.
* Injected the `IAuthorRepository` and `AuthorManager` to use in the service methods.
diff --git a/docs/en/tutorials/microservice/part-07.md b/docs/en/tutorials/microservice/part-07.md
index 5dc638dd04..a12b000415 100644
--- a/docs/en/tutorials/microservice/part-07.md
+++ b/docs/en/tutorials/microservice/part-07.md
@@ -216,7 +216,7 @@ Implementing `ITransientDependency` registers the `OrderEventHandler` class to t
### Testing the Order Creation
-To keep this tutorial simple, we will not implement a user interface for creating orders. Instead, we will use the Swagger UI to create an order. Open the *Solution Runner* panel in ABP Studio and use the *Start* action to launch the `CloudCrm.OrderingService` and `CloudCrm.CatalogService` applications. Then, use the *Start All* action to start the remaining applications listed in the [Solution Runner root item](../../studio/running-applications.md#run).
+To keep this tutorial simple, we will not implement a user interface for creating orders. Instead, we will use the Swagger UI to create an order. Open the *Solution Runner* panel in ABP Studio and use the *Start* action to launch the `CloudCrm.OrderingService` and `CloudCrm.CatalogService` applications. Then, use the *Start All* action to start the remaining applications listed in the [Solution Runner root item](../../studio/running-applications.md#startall).
Once the application is running and ready, [Browse](../../studio/running-applications.md#c-application) the `CloudCrm.OrderingService` application. Use the `POST /api/ordering/order` endpoint to create a new order:
diff --git a/framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientAccessor.cs b/framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientAccessor.cs
index 247e6e354e..dfe79d7e01 100644
--- a/framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientAccessor.cs
+++ b/framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientAccessor.cs
@@ -29,5 +29,13 @@ public class ChatClientAccessor : IChatClientAccessor
ChatClient = serviceProvider.GetKeyedService(
AbpAIWorkspaceOptions.GetChatClientServiceKeyName(
WorkspaceNameAttribute.GetWorkspaceName()));
+
+ // Fallback to default chat client if not configured for the workspace.
+ if (ChatClient is null)
+ {
+ ChatClient = serviceProvider.GetKeyedService(
+ AbpAIWorkspaceOptions.GetChatClientServiceKeyName(
+ AbpAIModule.DefaultWorkspaceName));
+ }
}
}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AI/Volo/Abp/AI/TypedChatClient.cs b/framework/src/Volo.Abp.AI/Volo/Abp/AI/TypedChatClient.cs
index 72ccfdaf38..9a52f7edc9 100644
--- a/framework/src/Volo.Abp.AI/Volo/Abp/AI/TypedChatClient.cs
+++ b/framework/src/Volo.Abp.AI/Volo/Abp/AI/TypedChatClient.cs
@@ -9,9 +9,13 @@ public class TypedChatClient : DelegatingChatClient, IChatClient(
+ serviceProvider.GetKeyedService(
AbpAIWorkspaceOptions.GetChatClientServiceKeyName(
WorkspaceNameAttribute.GetWorkspaceName()))
+ ??
+ serviceProvider.GetRequiredKeyedService(
+ AbpAIWorkspaceOptions.GetChatClientServiceKeyName(
+ AbpAIModule.DefaultWorkspaceName))
)
{
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiCurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiCurrentApplicationConfigurationCacheResetService.cs
index ad2f6ba983..26e51dbd38 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiCurrentApplicationConfigurationCacheResetService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiCurrentApplicationConfigurationCacheResetService.cs
@@ -1,3 +1,4 @@
+using System;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Components.Web.Configuration;
using Volo.Abp.DependencyInjection;
@@ -16,7 +17,7 @@ public class MauiCurrentApplicationConfigurationCacheResetService :
_mauiBlazorCachedApplicationConfigurationClient = mauiBlazorCachedApplicationConfigurationClient;
}
- public async Task ResetAsync()
+ public async Task ResetAsync(Guid? userId = null)
{
await _mauiBlazorCachedApplicationConfigurationClient.InitializeAsync();
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs
index 02de9d8bf7..144d975cb5 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs
@@ -1,4 +1,5 @@
-using System.Threading.Tasks;
+using System;
+using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Components.Web.Configuration;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.DependencyInjection;
@@ -19,10 +20,8 @@ public class BlazorServerCurrentApplicationConfigurationCacheResetService :
_localEventBus = localEventBus;
}
- public async Task ResetAsync()
+ public async Task ResetAsync(Guid? userId = null)
{
- await _localEventBus.PublishAsync(
- new CurrentApplicationConfigurationCacheResetEventData()
- );
+ await _localEventBus.PublishAsync(new CurrentApplicationConfigurationCacheResetEventData(userId));
}
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageLayout.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageLayout.cs
index fc7d372b37..dc51022956 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageLayout.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageLayout.cs
@@ -31,6 +31,8 @@ public class PageLayout : IScopedDependency, INotifyPropertyChanged
}
}
+ public bool ShowToolbar { get; set; } = true;
+
public virtual ObservableCollection BreadcrumbItems { get; } = new();
public virtual ObservableCollection ToolbarItems { get; } = new();
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/SimplePageToolbarContributor.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/SimplePageToolbarContributor.cs
index 647538c121..6e47281a70 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/SimplePageToolbarContributor.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/PageToolbars/SimplePageToolbarContributor.cs
@@ -16,6 +16,8 @@ public class SimplePageToolbarContributor : IPageToolbarContributor
public string? RequiredPolicyName { get; }
+ private bool? _shouldAddComponent;
+
public SimplePageToolbarContributor(
Type componentType,
Dictionary? arguments = null,
@@ -38,15 +40,19 @@ public class SimplePageToolbarContributor : IPageToolbarContributor
protected virtual async Task ShouldAddComponentAsync(PageToolbarContributionContext context)
{
- if (RequiredPolicyName != null)
+ if (_shouldAddComponent.HasValue)
+ {
+ return _shouldAddComponent.Value;
+ }
+
+ if (RequiredPolicyName == null)
{
- var authorizationService = context.ServiceProvider.GetRequiredService();
- if (!await authorizationService.IsGrantedAsync(RequiredPolicyName))
- {
- return false;
- }
+ _shouldAddComponent = true;
+ return _shouldAddComponent.Value;
}
- return true;
+ var authorizationService = context.ServiceProvider.GetRequiredService();
+ _shouldAddComponent = await authorizationService.IsGrantedAsync(RequiredPolicyName);
+ return _shouldAddComponent.Value;
}
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/ICurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/ICurrentApplicationConfigurationCacheResetService.cs
index c3e33a9e41..2d44399f9d 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/ICurrentApplicationConfigurationCacheResetService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/ICurrentApplicationConfigurationCacheResetService.cs
@@ -1,8 +1,9 @@
-using System.Threading.Tasks;
+using System;
+using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Components.Web.Configuration;
public interface ICurrentApplicationConfigurationCacheResetService
{
- Task ResetAsync();
+ Task ResetAsync(Guid? userId = null);
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/NullCurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/NullCurrentApplicationConfigurationCacheResetService.cs
index bb91d70775..1cfaee3315 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/NullCurrentApplicationConfigurationCacheResetService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/NullCurrentApplicationConfigurationCacheResetService.cs
@@ -1,3 +1,4 @@
+using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
@@ -5,7 +6,7 @@ namespace Volo.Abp.AspNetCore.Components.Web.Configuration;
public class NullCurrentApplicationConfigurationCacheResetService : ICurrentApplicationConfigurationCacheResetService, ISingletonDependency
{
- public Task ResetAsync()
+ public Task ResetAsync(Guid? userId = null)
{
return Task.CompletedTask;
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Configuration/BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Configuration/BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService.cs
index 40ac508030..359678daf4 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Configuration/BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Configuration/BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService.cs
@@ -1,3 +1,4 @@
+using System;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Components.Web.Configuration;
using Volo.Abp.DependencyInjection;
@@ -16,7 +17,7 @@ public class BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService :
_webAssemblyCachedApplicationConfigurationClient = webAssemblyCachedApplicationConfigurationClient;
}
- public async Task ResetAsync()
+ public async Task ResetAsync(Guid? userId = null)
{
await _webAssemblyCachedApplicationConfigurationClient.InitializeAsync();
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs
index cc1180fd20..ea0d778fad 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs
@@ -1,13 +1,26 @@
-using System.Globalization;
-using Volo.Abp.Users;
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Volo.Abp.Caching;
+using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.Client;
-public static class MvcCachedApplicationConfigurationClientHelper
+public class MvcCachedApplicationConfigurationClientHelper : ITransientDependency
{
- public static string CreateCacheKey(ICurrentUser currentUser)
+ protected IDistributedCache ApplicationVersionCache { get; }
+
+ public MvcCachedApplicationConfigurationClientHelper(IDistributedCache applicationVersionCache)
+ {
+ ApplicationVersionCache = applicationVersionCache;
+ }
+
+ public virtual async Task CreateCacheKeyAsync(Guid? userId)
{
- var userKey = currentUser.Id?.ToString("N") ?? "Anonymous";
- return $"ApplicationConfiguration_{userKey}_{CultureInfo.CurrentUICulture.Name}";
+ var appVersion = await ApplicationVersionCache.GetOrAddAsync(MvcCachedApplicationVersionCacheItem.CacheKey,
+ () => Task.FromResult(new MvcCachedApplicationVersionCacheItem(Guid.NewGuid().ToString("N")))) ??
+ new MvcCachedApplicationVersionCacheItem(Guid.NewGuid().ToString("N"));
+ var userKey = userId?.ToString("N") ?? "Anonymous";
+ return $"ApplicationConfiguration_{appVersion.Version}_{userKey}_{CultureInfo.CurrentUICulture.Name}";
}
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationVersionCacheItem.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationVersionCacheItem.cs
new file mode 100644
index 0000000000..1cd9990a44
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationVersionCacheItem.cs
@@ -0,0 +1,13 @@
+namespace Volo.Abp.AspNetCore.Mvc.Client;
+
+public class MvcCachedApplicationVersionCacheItem
+{
+ public const string CacheKey = "Mvc_Application_Version";
+
+ public string Version { get; set; }
+
+ public MvcCachedApplicationVersionCacheItem(string version)
+ {
+ Version = version;
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs
index 8d787bec65..ba42b55d18 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs
@@ -20,6 +20,7 @@ public class RemoteDynamicClaimsPrincipalContributorCache : RemoteDynamicClaimsP
protected IHttpClientFactory HttpClientFactory { get; }
protected IRemoteServiceHttpClientAuthenticator HttpClientAuthenticator { get; }
protected IDistributedCache ApplicationConfigurationDtoCache { get; }
+ protected MvcCachedApplicationConfigurationClientHelper CacheHelper { get; }
protected ICurrentUser CurrentUser { get; }
public RemoteDynamicClaimsPrincipalContributorCache(
@@ -28,7 +29,8 @@ public class RemoteDynamicClaimsPrincipalContributorCache : RemoteDynamicClaimsP
IOptions abpClaimsPrincipalFactoryOptions,
IRemoteServiceHttpClientAuthenticator httpClientAuthenticator,
IDistributedCache applicationConfigurationDtoCache,
- ICurrentUser currentUser)
+ ICurrentUser currentUser,
+ MvcCachedApplicationConfigurationClientHelper cacheHelper)
: base(abpClaimsPrincipalFactoryOptions)
{
Cache = cache;
@@ -36,6 +38,7 @@ public class RemoteDynamicClaimsPrincipalContributorCache : RemoteDynamicClaimsP
HttpClientAuthenticator = httpClientAuthenticator;
ApplicationConfigurationDtoCache = applicationConfigurationDtoCache;
CurrentUser = currentUser;
+ CacheHelper = cacheHelper;
}
protected async override Task GetCacheAsync(Guid userId, Guid? tenantId = null)
@@ -56,7 +59,7 @@ public class RemoteDynamicClaimsPrincipalContributorCache : RemoteDynamicClaimsP
catch (Exception e)
{
Logger.LogWarning(e, $"Failed to refresh remote claims for user: {userId}");
- await ApplicationConfigurationDtoCache.RemoveAsync(MvcCachedApplicationConfigurationClientHelper.CreateCacheKey(CurrentUser));
+ await ApplicationConfigurationDtoCache.RemoveAsync(await CacheHelper.CreateCacheKeyAsync(CurrentUser.Id));
throw;
}
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs
index 0bff9d09b1..e3d3c12370 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs
@@ -1,3 +1,4 @@
+using System;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
@@ -13,14 +14,18 @@ namespace Volo.Abp.AspNetCore.Mvc.Client;
public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigurationClient, ITransientDependency
{
+ private const string ApplicationConfigurationDtoCacheKey = "ApplicationConfigurationDto_CacheKey";
+
protected IHttpContextAccessor HttpContextAccessor { get; }
protected AbpApplicationConfigurationClientProxy ApplicationConfigurationAppService { get; }
protected AbpApplicationLocalizationClientProxy ApplicationLocalizationClientProxy { get; }
protected ICurrentUser CurrentUser { get; }
+ protected MvcCachedApplicationConfigurationClientHelper CacheHelper { get; }
protected IDistributedCache Cache { get; }
protected AbpAspNetCoreMvcClientCacheOptions Options { get; }
public MvcCachedApplicationConfigurationClient(
+ MvcCachedApplicationConfigurationClientHelper cacheHelper,
IDistributedCache cache,
AbpApplicationConfigurationClientProxy applicationConfigurationAppService,
ICurrentUser currentUser,
@@ -33,13 +38,27 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu
HttpContextAccessor = httpContextAccessor;
ApplicationLocalizationClientProxy = applicationLocalizationClientProxy;
Options = options.Value;
+ CacheHelper = cacheHelper;
Cache = cache;
}
- public async Task GetAsync()
+ public virtual async Task GetAsync()
{
- var cacheKey = CreateCacheKey();
+ string? cacheKey = null;
var httpContext = HttpContextAccessor?.HttpContext;
+ if (httpContext != null && httpContext.Items[ApplicationConfigurationDtoCacheKey] is string key)
+ {
+ cacheKey = key;
+ }
+
+ if (cacheKey.IsNullOrWhiteSpace())
+ {
+ cacheKey = await CreateCacheKeyAsync();
+ if (httpContext != null)
+ {
+ httpContext.Items[ApplicationConfigurationDtoCacheKey] = cacheKey;
+ }
+ }
if (httpContext != null && httpContext.Items[cacheKey] is ApplicationConfigurationDto configuration)
{
@@ -86,8 +105,21 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu
public ApplicationConfigurationDto Get()
{
- var cacheKey = CreateCacheKey();
+ string? cacheKey = null;
var httpContext = HttpContextAccessor?.HttpContext;
+ if (httpContext != null && httpContext.Items[ApplicationConfigurationDtoCacheKey] is string key)
+ {
+ cacheKey = key;
+ }
+
+ if (cacheKey.IsNullOrWhiteSpace())
+ {
+ cacheKey = AsyncHelper.RunSync(CreateCacheKeyAsync);
+ if (httpContext != null)
+ {
+ httpContext.Items[ApplicationConfigurationDtoCacheKey] = cacheKey;
+ }
+ }
if (httpContext != null && httpContext.Items[cacheKey] is ApplicationConfigurationDto configuration)
{
@@ -97,8 +129,8 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu
return AsyncHelper.RunSync(GetAsync);
}
- protected virtual string CreateCacheKey()
+ protected virtual async Task CreateCacheKeyAsync()
{
- return MvcCachedApplicationConfigurationClientHelper.CreateCacheKey(CurrentUser);
+ return await CacheHelper.CreateCacheKeyAsync(CurrentUser.Id);
}
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCurrentApplicationConfigurationCacheResetEventHandler.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCurrentApplicationConfigurationCacheResetEventHandler.cs
index 8bd3971779..c32b63249c 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCurrentApplicationConfigurationCacheResetEventHandler.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCurrentApplicationConfigurationCacheResetEventHandler.cs
@@ -3,7 +3,6 @@ using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
-using Volo.Abp.Users;
namespace Volo.Abp.AspNetCore.Mvc.Client;
@@ -11,23 +10,29 @@ public class MvcCurrentApplicationConfigurationCacheResetEventHandler :
ILocalEventHandler,
ITransientDependency
{
- protected ICurrentUser CurrentUser { get; }
protected IDistributedCache Cache { get; }
+ protected IDistributedCache ApplicationVersionCache { get; }
+ protected MvcCachedApplicationConfigurationClientHelper CacheHelper { get; }
- public MvcCurrentApplicationConfigurationCacheResetEventHandler(ICurrentUser currentUser,
- IDistributedCache cache)
+ public MvcCurrentApplicationConfigurationCacheResetEventHandler(
+ IDistributedCache cache,
+ IDistributedCache applicationVersionCache,
+ MvcCachedApplicationConfigurationClientHelper cacheHelper)
{
- CurrentUser = currentUser;
Cache = cache;
+ ApplicationVersionCache = applicationVersionCache;
+ CacheHelper = cacheHelper;
}
public virtual async Task HandleEventAsync(CurrentApplicationConfigurationCacheResetEventData eventData)
{
- await Cache.RemoveAsync(CreateCacheKey());
- }
-
- protected virtual string CreateCacheKey()
- {
- return MvcCachedApplicationConfigurationClientHelper.CreateCacheKey(CurrentUser);
+ if (eventData.UserId.HasValue)
+ {
+ await Cache.RemoveAsync(await CacheHelper.CreateCacheKeyAsync(eventData.UserId));
+ }
+ else
+ {
+ await ApplicationVersionCache.RemoveAsync(MvcCachedApplicationVersionCacheItem.CacheKey);
+ }
}
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentApplicationConfigurationCacheResetEventData.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentApplicationConfigurationCacheResetEventData.cs
index a50cb7b136..fccc295429 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentApplicationConfigurationCacheResetEventData.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentApplicationConfigurationCacheResetEventData.cs
@@ -1,9 +1,21 @@
-namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
+using System;
+
+namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
///
/// This event is used to invalidate current user's cached configuration.
///
public class CurrentApplicationConfigurationCacheResetEventData
{
+ public Guid? UserId { get; set; }
+
+ public CurrentApplicationConfigurationCacheResetEventData()
+ {
+
+ }
+ public CurrentApplicationConfigurationCacheResetEventData(Guid? userId)
+ {
+ UserId = userId;
+ }
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs
index 9935b90903..f2fbb2e4bf 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Mvc.Rendering;
+using System;
+using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Collections.Generic;
using System.Text.Encodings.Web;
@@ -41,12 +42,12 @@ public class AbpBreadcrumbItemTagHelperService : AbpTagHelperService : AbpTagHelperSe
}
var span = new TagBuilder("span");
- span.InnerHtml.AppendHtml(TagHelper.Text!);
+ span.InnerHtml.Append(TagHelper.Text!);
output.Content.AppendHtml(span);
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs
index b8462f4b5b..dea75d44d7 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs
@@ -22,7 +22,7 @@ public class AbpCardBodyTagHelperService : AbpTagHelperService