Browse Source

Merge branch 'abpframework:dev' into dev

pull/21450/head
Celal Ak 2 years ago
committed by GitHub
parent
commit
bd6223495a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      Directory.Packages.props
  2. 385
      docs/en/Community-Articles/2024-12-01-OpenAI-Integration/POST.md
  3. BIN
      docs/en/Community-Articles/2024-12-01-OpenAI-Integration/chat-example.gif
  4. BIN
      docs/en/Community-Articles/2024-12-01-OpenAI-Integration/cover-image.png
  5. BIN
      docs/en/Community-Articles/2024-12-01-OpenAI-Integration/image-generation-example.gif
  6. BIN
      docs/en/Community-Articles/2024-12-01-OpenAI-Integration/rag-example-1.gif
  7. BIN
      docs/en/Community-Articles/2024-12-01-OpenAI-Integration/rag-example-2.gif
  8. BIN
      docs/en/Community-Articles/2024-12-01-OpenAI-Integration/sample-page.png
  9. 2
      docs/en/cli/index.md
  10. 29
      docs/en/docs-nav.json
  11. 2
      docs/en/framework/fundamentals/dependency-injection.md
  12. 2
      docs/en/framework/ui/angular/quick-start.md
  13. 2
      docs/en/framework/ui/mvc-razor-pages/client-side-package-management.md
  14. 2
      docs/en/framework/ui/react-native/index.md
  15. BIN
      docs/en/get-started/images/abp-studio-microservice-solution-runner-enable-watch-1.png
  16. BIN
      docs/en/get-started/images/abp-studio-microservice-solution-runner-enable-watch-2.png
  17. BIN
      docs/en/get-started/images/abp-studio-microservice-solution-runner-enable-watch.png
  18. BIN
      docs/en/get-started/images/abp-studio-no-layers-new-solution-additional-options-0.9.13.png
  19. BIN
      docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-0.9.13.png
  20. BIN
      docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-database-configurations-efcore-0.9.13.png
  21. BIN
      docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-database-configurations-mongo-0.9.13.png
  22. BIN
      docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-database-provider-efcore-0.9.13.png
  23. BIN
      docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-database-provider-mongo-0.9.13.png
  24. BIN
      docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-solution-properties-0.9.13.png
  25. BIN
      docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-ui-framework-0.9.13.png
  26. BIN
      docs/en/get-started/images/abp-studio-nolayers-new-solution-dialog-ui-theme-0.9.13.png
  27. 6
      docs/en/get-started/layered-web-application.md
  28. 8
      docs/en/get-started/microservice.md
  29. 28
      docs/en/get-started/single-layer-web-application.md
  30. BIN
      docs/en/images/generic-repositories.png
  31. BIN
      docs/en/images/idle-message.png
  32. BIN
      docs/en/images/idle-setting.png
  33. BIN
      docs/en/images/pen-test-alert-list-9.0.png
  34. 1
      docs/en/modules/account-pro.md
  35. 19
      docs/en/modules/account/idle-session-timeout.md
  36. 46
      docs/en/others/penetration-test-report.md
  37. 2
      docs/en/samples/easy-crm.md
  38. 4
      docs/en/studio/installation.md
  39. 8
      docs/en/studio/release-notes.md
  40. 2
      docs/en/studio/version-mapping.md
  41. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-generated-tests.png
  42. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-navigation-property.png
  43. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-opening.png
  44. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-solution-test-projects.png
  45. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/book-store-suite-solution-explorer.png
  46. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/bookstore-test-succeed.png
  47. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/studio-browser-suite.png
  48. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-author-entity-1.png
  49. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-author-entity-2.png
  50. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-author-new-entity.png
  51. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-author-pages-1.png
  52. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-1.png
  53. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-2.png
  54. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-3.png
  55. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-4.png
  56. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-5.png
  57. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-6.png
  58. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-selection.png
  59. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-book-pages-1.png
  60. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-book-with-author-create-modal.png
  61. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-bookstore-advanced-filter-section.png
  62. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-custom-code-result.png
  63. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-enabling-custom-code.png
  64. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-end-of-generation-modal.png
  65. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/suite-repository-custom-code.png
  66. BIN
      docs/en/tutorials/book-store-with-abp-suite/images/test-data-seed-contributors.png
  67. 40
      docs/en/tutorials/book-store-with-abp-suite/index.md
  68. 40
      docs/en/tutorials/book-store-with-abp-suite/part-01.md
  69. 123
      docs/en/tutorials/book-store-with-abp-suite/part-02.md
  70. 79
      docs/en/tutorials/book-store-with-abp-suite/part-03.md
  71. 168
      docs/en/tutorials/book-store-with-abp-suite/part-04.md
  72. 123
      docs/en/tutorials/book-store-with-abp-suite/part-05.md
  73. 2
      docs/en/tutorials/book-store/part-01.md
  74. 6
      docs/en/tutorials/todo/layered/index.md
  75. 6
      docs/en/tutorials/todo/single-layer/index.md
  76. 32
      docs/en/ui-themes/lepton-x/blazor.md
  77. 2
      docs/en/ui-themes/lepton-x/index.md
  78. 296
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/DaprAspNetCore/AbpDaprEndpointRouteBuilderExtensions.cs
  79. 135
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusModule.cs
  80. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubConsts.cs
  81. 62
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Controllers/AbpAspNetCoreMvcDaprEventsController.cs
  82. 4
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs
  83. 3
      framework/src/Volo.Abp.BlazoriseUI/Components/EntityAction.razor
  84. 8
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/DistributedEventBusBase.cs
  85. 11
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientOptions.cs
  86. 11
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs
  87. 6
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs
  88. 1
      framework/src/Volo.Abp.Swashbuckle/Microsoft/AspNetCore/Builder/AbpSwaggerUIBuilderExtensions.cs
  89. 4
      framework/src/Volo.Abp.Swashbuckle/Volo/Abp/Swashbuckle/SwaggerHtmlResolver.cs
  90. 12
      framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/abp.swagger.js
  91. 75
      framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/index.js
  92. 16
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs
  93. 3
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs
  94. 8
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs
  95. 8
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs
  96. 2
      latest-versions.json
  97. 6
      modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json
  98. 276
      modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock
  99. 4
      modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json
  100. 280
      modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock

4
Directory.Packages.props

@ -39,8 +39,8 @@
<PackageVersion Include="EphemeralMongo6.runtime.win-x64" Version="1.1.3" />
<PackageVersion Include="FluentValidation" Version="11.10.0" />
<PackageVersion Include="Google.Cloud.Storage.V1" Version="4.10.0" />
<PackageVersion Include="Hangfire.AspNetCore" Version="1.8.14" />
<PackageVersion Include="Hangfire.SqlServer" Version="1.8.14" />
<PackageVersion Include="Hangfire.AspNetCore" Version="1.8.17" />
<PackageVersion Include="Hangfire.SqlServer" Version="1.8.17" />
<PackageVersion Include="HtmlSanitizer" Version="8.1.870" />
<PackageVersion Include="IdentityModel" Version="7.0.0" />
<PackageVersion Include="IdentityServer4" Version="4.1.2" />

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

@ -0,0 +1,385 @@
# How to Use OpenAI API with ABP Framework
In this article, I will show you how to integrate and use the [OpenAI API](https://github.com/openai/openai-dotnet?tab=readme-ov-file#getting-started) with the [ABP Framework](https://abp.io/). We will explore step-by-step how these technologies can work together to enhance your application with powerful AI capabilities, such as natural language processing, image generation, and more.
![cover-image](cover-image.png)
## Creating an ABP Project
To begin integrating OpenAI API with ABP Framework, you first need to create an ABP project. Follow these steps to create and set up your ABP project:
### Step 1: Install ABP CLI
The ABP CLI is a command-line interface tool that helps you create and manage ABP projects easily. To install the ABP CLI, run the following command in your terminal:
```bash
dotnet tool install -g Volo.Abp.Studio.Cli
```
### Step 2: Create a New ABP Project
Once you have installed the ABP CLI, you can create a new ABP project using the following command:
```bash
abp new Acme.OpenAIIntegration -t app --ui-framework mvc --database-provider ef -dbms PostgreSQL --csf
```
> This command will generate a complete ABP project with an [MVC UI](https://abp.io/docs/latest/framework/ui/mvc-razor-pages/overall). The examples provided in this article make use of UI controllers for demonstration purposes. However, the same approach can easily be applied to other UI types supported by ABP, such as Blazor or Angular. You can find other options [here](https://abp.io/docs/latest/cli).
## OpenAI Integration Setup
To begin integrating OpenAI API with ABP Framework, follow these steps:
### Step 1: Create an API Key
To use the OpenAI services, you first need an API key. To obtain one, first [create a new OpenAI account](https://platform.openai.com/signup) or [log in](https://platform.openai.com/login). Next, navigate to the [API key page](https://platform.openai.com/account/api-keys) and select "Create new secret key", optionally naming the key. Make sure to save your API key somewhere safe and do not share it with anyone.
This key will be used to authenticate your application when making requests to the OpenAI endpoints.
### Step 2: Adding *Microsoft.Extensions.AI* Package
To integrate OpenAI API with ABP, we use [Microsoft.Extensions.AI](https://www.nuget.org/packages/Microsoft.Extensions.AI.OpenAI/). This package offers a unified API for integrating AI services, making it easy for developers to work with different AI providers. You can find more details in [this blog post](https://devblogs.microsoft.com/dotnet/introducing-microsoft-extensions-ai-preview/).
To begin integrating OpenAI API with ABP Framework, follow these steps:
1. Add the **Microsoft.Extensions.AI** and **Microsoft.Extensions.AI.OpenAI** (used to interact specifically with OpenAI services. Additionally, this package has alternatives like [Azure OpenAI](https://www.nuget.org/packages/Microsoft.Extensions.AI.OpenAI/), [Azure AI Inference](https://www.nuget.org/packages/Microsoft.Extensions.AI.AzureAIInference/), and [Ollama](https://www.nuget.org/packages/Microsoft.Extensions.AI.Ollama/), offering flexibility for developers to choose the AI provider that best fits their needs) packages:
```bash
dotnet add package Microsoft.Extensions.AI --prerelease
dotnet add package Microsoft.Extensions.AI.OpenAI --prerelease
```
2. Add the required configuration to the `appsettings.json` file located inside the `Acme.OpenAIIntegration.Web` project and dependencies to your `ConfigureServices` method:
```json
"AI": {
"OpenAI": {
"Key": "YOUR-API-KEY",
"Chat": {
"ModelId": "gpt-4o-mini"
}
}
}
```
> Replace the value of the `Key` with your OpenAI API key.
Next, add the following code to the `ConfigureServices` method in `OpenAIIntegrationBlazorModule`:
```csharp
context.Services.AddSingleton(new OpenAIClient(configuration["AI:OpenAI:Key"]));
context.Services.AddChatClient(services =>
services.GetRequiredService<OpenAIClient>().AsChatClient(configuration["AI:OpenAI:Chat:ModelId"] ?? "gpt-4o-mini"));
```
## Creating a Sample Page
To demonstrate the use of OpenAI API, let's create a page named `Sample` in the `Acme.OpenAIIntegration.Web` project:
Create a `Sample` folder under the `Pages` folder of the `Acme.OpenAIIntegration.Web` project. Add a new Razor Page by right-clicking the `Sample` folder then selecting `Add > Razor Page`. Name it `Index`.
Open the `Index.cshtml` and change the whole content as shown below:
> Note: This example demonstrates a simple implementation of a sample page that interacts with the OpenAI API, covering chat, [retrieval-augmented generation (RAG)](https://github.com/openai/openai-dotnet?tab=readme-ov-file#how-to-use-assistants-with-retrieval-augmented-generation-rag), and image generation features. Each example is explained in detail in the next section, so feel free to continue for a better understanding of the steps and logic involved.
```html
@page
@model Acme.OpenAIIntegration.Web.Pages.Sample
@{
ViewData["Title"] = "OpenAI API Demonstration";
}
<h1>@ViewData["Title"]</h1>
<br/><br/>
<div class="row">
<div class="col-md-4">
<h2>Chat Example</h2>
<form method="post" asp-page-handler="Chat">
<div class="form-group">
<label asp-for="ChatInput">Enter your message:</label>
<textarea asp-for="ChatInput" class="form-control" rows="4"></textarea>
</div>
<button type="submit" class="btn btn-primary mt-2">Send</button>
</form>
@if (!string.IsNullOrEmpty(Model.ChatResponse))
{
<h3 class="mt-3">Response:</h3>
<p>@Model.ChatResponse</p>
}
</div>
<div class="col-md-4">
<h2>RAG Example</h2>
<form method="post" asp-page-handler="RAG">
<div class="form-group mt-2">
<label asp-for="RAGQuery">Query:</label>
<input asp-for="RAGQuery" class="form-control" />
</div>
<button type="submit" class="btn btn-primary mt-2">Ask</button>
</form>
@if (!string.IsNullOrEmpty(Model.RAGResponse))
{
<h3 class="mt-3">Result:</h3>
<p>@Model.RAGResponse</p>
}
</div>
<div class="col-md-4">
<h2>Image Generation Example</h2>
<form method="post" asp-page-handler="ImageGeneration">
<div class="form-group">
<label asp-for="ImagePrompt">Image Description:</label>
<input asp-for="ImagePrompt" class="form-control" />
</div>
<button type="submit" class="btn btn-primary mt-2">Generate Image</button>
</form>
@if (Model.GeneratedImageBytes != null)
{
<h3 class="mt-3">Generated Image:</h3>
<img src="data:image/png;base64,@Convert.ToBase64String(Model.GeneratedImageBytes)" alt="Generated image" class="img-fluid mt-2" />
}
</div>
</div>
```
`Index.cshtml.cs` content should be like that:
```csharp
using System;
using System.ClientModel;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Assistants;
using OpenAI.Files;
using OpenAI.Images;
namespace Acme.OpenAIIntegration.Web.Pages;
public class Sample : PageModel
{
[BindProperty]
public string ChatInput { get; set; }
public string ChatResponse { get; set; }
[BindProperty]
public string RAGQuery { get; set; }
public string RAGResponse { get; set; }
[BindProperty]
public string ImagePrompt { get; set; }
public byte[] GeneratedImageBytes { get; set; }
private readonly IChatClient _chatClient;
private readonly OpenAIClient _openAiClient;
public Sample(
IChatClient chatClient,
OpenAIClient openAiClient)
{
_chatClient = chatClient;
_openAiClient = openAiClient;
}
public async Task<IActionResult> OnPostChatAsync()
{
ChatResponse = $"Chat response: {(await _chatClient.CompleteAsync(ChatInput)).Message}";
return Page();
}
public async Task<IActionResult> OnPostRAGAsync()
{
#pragma warning disable OPENAI001
var fileClient = _openAiClient.GetOpenAIFileClient();
var assistantClient = _openAiClient.GetAssistantClient();
using var document = BinaryData.FromBytes(GetExceptionHandlingDocumentContent().ToArray()).ToStream();
var exceptionHandlingDoc = await fileClient.UploadFileAsync(
document,
"ExceptionHandling.md",
FileUploadPurpose.Assistants);
AssistantCreationOptions assistantOptions = new()
{
Name = "Exception Handling Assistant",
Instructions =
"""
This assistant helps you with exception handling in ABP Framework. You can ask questions about exception handling and get answers.
- Do not make any assumptions when asked for information that is not in the document
- Give the most accurate information possible
- Give short(max 1-2 sentence) and concise answers
- Do not provide file citations
""",
Tools =
{
new FileSearchToolDefinition(),
},
ToolResources = new()
{
FileSearch = new()
{
NewVectorStores =
{
new VectorStoreCreationHelper([exceptionHandlingDoc.Value.Id]),
}
}
},
};
var assistant = await assistantClient.CreateAssistantAsync("gpt-4o", assistantOptions);
ThreadCreationOptions threadOptions = new()
{
InitialMessages = { RAGQuery }
};
ThreadRun threadRun = assistantClient.CreateThreadAndRun(assistant.Value.Id, threadOptions);
do
{
Thread.Sleep(TimeSpan.FromSeconds(1));
threadRun = assistantClient.GetRun(threadRun.ThreadId, threadRun.Id);
} while (!threadRun.Status.IsTerminal);
CollectionResult<ThreadMessage> messages
= assistantClient.GetMessages(threadRun.ThreadId,
new MessageCollectionOptions() { Order = MessageCollectionOrder.Ascending });
var response = new StringBuilder();
foreach (var message in messages)
{
response.AppendLine($"[{message.Role.ToString().ToUpper()}]: ");
foreach (var contentItem in message.Content)
{
if (!string.IsNullOrEmpty(contentItem.Text))
{
response.AppendLine(contentItem.Text);
if (contentItem.TextAnnotations.Count > 0)
{
response.AppendLine("");
}
}
}
response.AppendLine("");
#pragma warning restore OPENAI001
}
RAGResponse = response.ToString();
return Page();
}
public async Task<IActionResult> OnPostImageGenerationAsync()
{
var client = _openAiClient.GetImageClient("dall-e-3");
var image = await client.GenerateImageAsync(ImagePrompt, new ImageGenerationOptions
{
ResponseFormat = GeneratedImageFormat.Bytes
});
var imageBytes = image.Value.ImageBytes;
using var memoryStream = new MemoryStream();
await imageBytes.ToStream().CopyToAsync(memoryStream);
GeneratedImageBytes = memoryStream.ToArray();
return Page();
}
public ReadOnlySpan<byte> GetExceptionHandlingDocumentContent()
{
return """
# Exception Handling
ABP provides a built-in infrastructure and offers a standard model for handling exceptions.
* Automatically **handles all exceptions** and sends a standard **formatted error message** to the client for an API/AJAX request.
* Automatically hides **internal infrastructure errors** and returns a standard error message.
* Provides an easy and configurable way to **localize** exception messages.
* Automatically maps standard exceptions to **HTTP status codes** and provides a configurable option to map custom exceptions.
## Automatic Exception Handling
`AbpExceptionFilter` handles an exception if **any of the following conditions** are met:
* Exception is thrown by a **controller action** which returns an **object result** (not a view result).
* The request is an AJAX request (`X-Requested-With` HTTP header value is `XMLHttpRequest`).
* Client explicitly accepts the `application/json` content type (via `accept` HTTP header).
If the exception is handled it's automatically **logged** and a formatted **JSON message** is returned to the client.
## Business Exceptions
Most of your own exceptions will be business exceptions. The `IBusinessException` interface is used to mark an exception as a business exception.
`BusinessException` implements the `IBusinessException` interface in addition to the `IHasErrorCode`, `IHasErrorDetails` and `IHasLogLevel` interfaces. The default log level is `Warning`.
Usually you have an error code related to a particular business exception. For example:
````C#
throw new BusinessException(QaErrorCodes.CanNotVoteYourOwnAnswer);
````
### User Friendly Exception
If an exception implements the `IUserFriendlyException` interface, then ABP does not change it's `Message` and `Details` properties and directly send it to the client.
`UserFriendlyException` class is the built-in implementation of the `IUserFriendlyException` interface. Example usage:
````C#
throw new UserFriendlyException(
"Username should be unique!"
);
````
* The `IUserFriendlyException` interface is derived from the `IBusinessException` and the `UserFriendlyException` class is derived from the `BusinessException` class.
"""u8;
}
}
```
## Running the Application
After completing the setup, you can run the application using the following command:
```bash
dotnet run --project ./src/Acme.OpenAIIntegration.Web
```
Once the application is running, open your browser and navigate to `/Sample`. You should see the `Sample` page we created, which contains sections for Chat, RAG (Retrieval-Augmented Generation), and Image Generation. You can find the screenshot of the page below:
![sample page](sample-page.png)
## Examples Overview
To showcase the integration of the OpenAI API with the ABP Framework, we implemented three different examples:
1. **Chat Example**: This example demonstrates how to use OpenAI's chat capabilities by allowing users to enter a message and receive an AI-generated response. The implementation involves setting up a simple form on the `Sample` page where users can input their message. The form submission triggers the `OnPostChatAsync` method, which uses the `IChatClient` to generate a response.
![chat-example](chat-example.gif)
2. **Retrieval-Augmented Generation (RAG) Example**: In this example, we use OpenAI to answer user queries by referencing custom documents uploaded to the OpenAI API. The implementation involves uploading a document using the `OpenAIFileClient` and creating an assistant with specific instructions to handle the uploaded content. In this case, the document is a section from ABP's Exception Handling documentation, which includes examples on how ABP handles exceptions, user-friendly error messages, and business exceptions. Users can input their query on the `Sample` page, and the `OnPostRAGAsync` method processes the query to generate precise answers based on the document content. If users ask questions that are not covered in the document, the assistant clearly indicates that the information is not available, as per the instructions provided. For example, when asked about `Object Extensions`, the response begins with: "The uploaded document does not contain information about `Object Extensions`...". This demonstrates how the assistant adheres to the provided instructions. You can also find this example illustrated in the GIF below.
![rag-example-1](rag-example-1.gif)
![rag-example-2](rag-example-2.gif)
3. **Image Generation Example**: This example leverages the [DALL-E](https://openai.com/index/dall-e-3/) model to generate images based on user-provided prompts. On the `Sample` page, users can provide a description of the image they want to generate, and the `OnPostImageGenerationAsync` method uses the `OpenAIClient` to generate the image.
![image-generation-example](image-generation-example.gif)
## Conclusion
In this article, we covered how to integrate the OpenAI API with the ABP Framework by creating a sample project, setting up the OpenAI services, and implementing examples for conversational AI, knowledge-based assistance, and image generation. By following these steps, you can add powerful AI-driven capabilities to your application, making it more interactive, intelligent, and capable of meeting user needs effectively.

BIN
docs/en/Community-Articles/2024-12-01-OpenAI-Integration/chat-example.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

BIN
docs/en/Community-Articles/2024-12-01-OpenAI-Integration/cover-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 KiB

BIN
docs/en/Community-Articles/2024-12-01-OpenAI-Integration/image-generation-example.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 KiB

BIN
docs/en/Community-Articles/2024-12-01-OpenAI-Integration/rag-example-1.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

BIN
docs/en/Community-Articles/2024-12-01-OpenAI-Integration/rag-example-2.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

BIN
docs/en/Community-Articles/2024-12-01-OpenAI-Integration/sample-page.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

2
docs/en/cli/index.md

@ -211,6 +211,7 @@ For more samples, go to [ABP CLI Create Solution Samples](new-command-samples.md
* `leptonx`: LeptonX Theme.
* `basic`: Basic Theme.
* `--public-website`: Public Website is a front-facing website for describing your project, listing your products and doing SEO for marketing purposes. Users can login and register on your website with this website. This option is only included in PRO templates.
* `--no-grafana-dashboard` or `-ngd`: Does not add example Grafana Dashboard to the solution.
* `--output-folder` or `-o`: Specifies the output folder. Default value is the current directory.
* `--local-framework-ref` or `-lfr`: Uses local projects references to the ABP framework instead of using the NuGet packages. It tries to find the paths from `ide-state.json`. The file is located at `%UserProfile%\.abp\studio\ui\ide-state.json` (for Windows) and `~/.abp/studio/ui/ide-state.json` (for MAC).
* `--create-solution-folder` or `-csf`: Specifies if the project will be in a new folder in the output folder or directly the output folder.
@ -225,6 +226,7 @@ For more samples, go to [ABP CLI Create Solution Samples](new-command-samples.md
* `--dont-run-bundling`: Skip bundling for Blazor packages.
* `--no-kubernetes-configuration` or `-nkc`: Skips the Kubernetes configuration files.
* `--no-social-logins` or `-nsl`: Skipts the social login configuration.
* `--no-tests` or `-ntp`: Does not add test projects.
* *Module Options*: You can skip some modules if you don't want to add them to your solution (*Available for* ***Team*** *or higher licenses*). Available commands:
* `-no-saas`: Skips the Saas module.
* `-no-gdpr`: Skips the GDPR module.

29
docs/en/docs-nav.json

@ -115,6 +115,35 @@
}
]
},
{
"text": "Book Store Application (with ABP Suite)",
"items": [
{
"text": "Overview",
"path": "tutorials/book-store-with-abp-suite"
},
{
"text": "1: Creating the Solution",
"path": "tutorials/book-store-with-abp-suite/part-01.md"
},
{
"text": "2: Creating the Books",
"path": "tutorials/book-store-with-abp-suite/part-02.md"
},
{
"text": "3: Creating the Authors",
"path": "tutorials/book-store-with-abp-suite/part-03.md"
},
{
"text": "4: Book to Author Relation",
"path": "tutorials/book-store-with-abp-suite/part-04.md"
},
{
"text": "5: Customizing the Generated Code",
"path": "tutorials/book-store-with-abp-suite/part-05.md"
}
]
},
{
"text": "Modular Monolith Application",
"items": [

2
docs/en/framework/fundamentals/dependency-injection.md

@ -498,6 +498,8 @@ Use `ICachedServiceProvider` (instead of `ITransientCachedServiceProvider`) unle
> ABP also provides the `IAbpLazyServiceProvider` service. It does exists for backward compatibility and works exactly same with the `ITransientCachedServiceProvider` service. So, use the `ITransientCachedServiceProvider` since the `IAbpLazyServiceProvider` might be removed in future ABP versions.
> Another advantage of using `ICachedServiceProvider` is that, during an HTTP request, if a service's constructor requires injecting many dependencies, it can negatively impact performance, as the injected services may not all be used by the current request. By resolving services on-demand, performance degradation can be effectively avoided.
## Advanced Features
### IServiceCollection.OnRegistered Event

2
docs/en/framework/ui/angular/quick-start.md

@ -6,7 +6,7 @@
Please follow the steps below to prepare your development environment for Angular.
1. **Install Node.js:** Please visit [Node.js downloads page](https://nodejs.org/en/download/) and download proper Node.js `v18.19+` installer for your OS. An alternative is to install [NVM](https://github.com/nvm-sh/nvm) and use it to have multiple versions of Node.js in your operating system.
1. **Install Node.js:** Please visit [Node.js downloads page](https://nodejs.org/en/download/) and download proper Node.js `v20.11+` installer for your OS. An alternative is to install [NVM](https://github.com/nvm-sh/nvm) and use it to have multiple versions of Node.js in your operating system.
2. **[Optional] Install Yarn:** You may install Yarn v1.22+ (not v2) following the instructions on [the installation page](https://classic.yarnpkg.com/en/docs/install). Yarn v1 delivers an arguably better developer experience compared to npm v10 and below. You may skip this step and work with npm, which is built-in in Node.js, instead.
3. **[Optional] Install VS Code:** [VS Code](https://code.visualstudio.com/) is a free, open-source IDE which works seamlessly with TypeScript. Although you can use any IDE including Visual Studio or Rider, VS Code will most likely deliver the best developer experience when it comes to Angular projects. ABP project templates even contain plugin recommendations for VS Code users, which VS Code will ask you to install when you open the Angular project folder. Here is a list of recommended extensions:
- [Angular Language Service](https://marketplace.visualstudio.com/items?itemName=angular.ng-template)

2
docs/en/framework/ui/mvc-razor-pages/client-side-package-management.md

@ -41,7 +41,7 @@ After depending on a NPM package, all you should do is to run the **yarn** comma
yarn
```
Alternatively, you can use `npm install` but [Yarn](https://classic.yarnpkg.com/) is suggested as mentioned before.
Alternatively, you can use `npm install` but [Yarn v1.22+ (not v2)](https://classic.yarnpkg.com/en/docs/install) is suggested as mentioned before.
#### Package Contribution

2
docs/en/framework/ui/react-native/index.md

@ -17,7 +17,7 @@ ABP platform provide basic [React Native](https://reactnative.dev/) startup temp
Please follow the steps below to prepare your development environment for React Native.
1. **Install Node.js:** Please visit [Node.js downloads page](https://nodejs.org/en/download/) and download proper Node.js v16 or v18 installer for your OS. An alternative is to install [NVM](https://github.com/nvm-sh/nvm) and use it to have multiple versions of Node.js in your operating system.
1. **Install Node.js:** Please visit [Node.js downloads page](https://nodejs.org/en/download/) and download proper Node.js v20.11+ installer for your OS. An alternative is to install [NVM](https://github.com/nvm-sh/nvm) and use it to have multiple versions of Node.js in your operating system.
2. **[Optional] Install Yarn:** You may install Yarn v1 (not v2) following the instructions on [the installation page](https://classic.yarnpkg.com/en/docs/install). Yarn v1 delivers an arguably better developer experience compared to npm v6 and below. You may skip this step and work with npm, which is built-in in Node.js, instead.
3. **[Optional] Install VS Code:** [VS Code](https://code.visualstudio.com/) is a free, open-source IDE which works seamlessly with TypeScript. Although you can use any IDE including Visual Studio or Rider, VS Code will most likely deliver the best developer experience when it comes to React Native projects.
4. **Install an Emulator:** React Native applications need an Android emulator or an iOS simulator to run on your OS. See the [Android Studio Emulator](https://docs.expo.io/workflow/android-simulator/) or [iOS Simulator](https://docs.expo.io/workflow/ios-simulator/) on expo.io documentation to learn how to set up an emulator.

BIN
docs/en/get-started/images/abp-studio-microservice-solution-runner-enable-watch-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/en/get-started/images/abp-studio-microservice-solution-runner-enable-watch-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/en/get-started/images/abp-studio-microservice-solution-runner-enable-watch.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/get-started/images/abp-studio-no-layers-new-solution-additional-options-0.9.13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-0.9.13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-database-configurations-efcore-0.9.13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-database-configurations-mongo-0.9.13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-database-provider-efcore-0.9.13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-database-provider-mongo-0.9.13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-solution-properties-0.9.13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
docs/en/get-started/images/abp-studio-no-layers-new-solution-dialog-ui-framework-0.9.13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

BIN
docs/en/get-started/images/abp-studio-nolayers-new-solution-dialog-ui-theme-0.9.13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

6
docs/en/get-started/layered-web-application.md

@ -19,10 +19,10 @@ First things first! Let's setup your development environment before creating the
The following tools should be installed on your development machine:
* [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) (v17.3+) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/). <sup id="a-editor">[1](#f-editor)</sup>
* [.NET 8.0+](https://dotnet.microsoft.com/en-us/download/dotnet)
* [Visual Studio 2022](https://visualstudio.microsoft.com/) or another IDE that supports [.NET 9.0+](https://dotnet.microsoft.com/download/dotnet) development. <sup id="a-editor">[1](#f-editor)</sup>
* [.NET 9.0+](https://dotnet.microsoft.com/en-us/download/dotnet)
{{ if UI != "Blazor" }}
* [Node v18.19+](https://nodejs.org/)
* [Node v20.11+](https://nodejs.org/)
* [Yarn v1.22+ (not v2)](https://classic.yarnpkg.com/en/docs/install) <sup id="a-yarn">[2](#f-yarn)</sup> or npm v10+ (already installed with Node)
{{ end }}
{{ if Tiered == "Yes" }}

8
docs/en/get-started/microservice.md

@ -171,9 +171,11 @@ Once you run the `IdentityService` in Visual Studio, it will be completely integ
As an alternative approach, especially if you don't need to debug your service, you can enable the watching feature of ABP Studio to automatically re-build and re-start when there is change in your application/service.
To enable watching, right-click the application/service you want to watch, select the *Run* -> *Enable Watch* command as shown in the following figure:
To enable watching, right-click the application/service you want to watch, select the *Properties* -> *Watch changes while running* option as shown in the following figure:
![abp-studio-microservice-solution-runner-enable-watch](images/abp-studio-microservice-solution-runner-enable-watch.png)
![abp-studio-microservice-solution-runner-enable-watch-1](images/abp-studio-microservice-solution-runner-enable-watch-1.png)
![abp-studio-microservice-solution-runner-enable-watch-2](images/abp-studio-microservice-solution-runner-enable-watch-2.png)
Now, you can make your development on the `IdentityService`. Whenever you save a code file, it is automatically rebuilt and restarted by ABP Studio, so any change will be effective on the running solution in a few seconds.
@ -181,8 +183,6 @@ When you enable watch for an application an *eye* icon is added near to the appl
![abp-studio-microservice-solution-runner-watch-enabled-icon](images/abp-studio-microservice-solution-runner-watch-enabled-icon.png)
You can disable watching by right-clicking an application and selecting *Run* -> *Disable Watch* command.
## Kubernetes Integration: Working with Helm Charts
Solution Runner is a great way to locally run all the applications and services of your solution. However, there are some drawbacks:

28
docs/en/get-started/single-layer-web-application.md

@ -18,10 +18,10 @@ First things first! Let's setup your development environment before creating the
The following tools should be installed on your development machine:
* [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) (v17.3+) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/). <sup id="a-editor">[1](#f-editor)</sup>
* [.NET 8.0+](https://dotnet.microsoft.com/en-us/download/dotnet)
* [Visual Studio 2022](https://visualstudio.microsoft.com/) or another IDE that supports [.NET 9.0+](https://dotnet.microsoft.com/download/dotnet) development. <sup id="a-editor">[1](#f-editor)</sup>
* [.NET 9.0+](https://dotnet.microsoft.com/en-us/download/dotnet)
{{ if UI != "Blazor" }}
* [Node v18.19+](https://nodejs.org/)
* [Node v20.11+](https://nodejs.org/)
* [Yarn v1.22+ (not v2)](https://classic.yarnpkg.com/en/docs/install) <sup id="a-yarn">[2](#f-yarn)</sup> or npm v10+ (already installed with Node)
{{ end }}
{{ if Tiered == "Yes" }}
@ -50,11 +50,11 @@ Assuming that you have [installed and logged in](../studio/installation.md) to t
Select the *File* -> *New Solution* in the main menu, or click the *New solution* button on the Welcome screen to open the *Create new solution* wizard:
![abp-studio-new-solution-dialog](images/abp-studio-no-layers-new-solution-dialog.png)
![abp-studio-new-solution-dialog](images/abp-studio-no-layers-new-solution-dialog-0.9.13.png)
We will use the *Application (Single Layer)* solution template for this tutorial, so pick it and click the *Next* button:
![abp-studio-new-solution-dialog-solution-properties](images/abp-studio-no-layers-new-solution-dialog-solution-properties.png)
![abp-studio-new-solution-dialog-solution-properties](images/abp-studio-no-layers-new-solution-dialog-solution-properties-0.9.13.png)
On that screen, you choose a name for your solution. You can use different levels of namespaces; e.g. `BookStore`, `Acme.BookStore` or `Acme.Retail.BookStore`.
@ -62,31 +62,35 @@ Then select an *output folder* to create your solution. The *Create solution fol
Once your configuration is done, click the *Next* button to navigate to the *UI Framework* selection:
![abp-studio-new-solution-dialog-ui-framework](images/abp-studio-no-layers-new-solution-dialog-ui-framework.png)
![abp-studio-new-solution-dialog-ui-framework](images/abp-studio-no-layers-new-solution-dialog-ui-framework-0.9.13.png)
Here, you see all the possible UI options supported by that startup solution template. Pick the **{{ UI_Value }}**.
Notice that; Once you select a UI type, some additional options will be available under the UI Framework list. You can further configure the options or leave them as default and click the *Next* button for the *UI Theme* selection screen:
![abp-studio-new-solution-dialog-ui-theme](images/abp-studio-nolayers-new-solution-dialog-ui-theme.png)
![abp-studio-new-solution-dialog-ui-theme](images/abp-studio-nolayers-new-solution-dialog-ui-theme-0.9.13.png)
LeptonX is the suggested UI theme that is proper for production usage. Select one of the themes, configure the additional options, and click the *Next* button for the *Database Provider* selection:
{{ if DB == "EF" }}
![abp-studio-new-solution-dialog-database-provider](images/abp-studio-no-layers-new-solution-dialog-database-provider-efcore.png)
![abp-studio-new-solution-dialog-database-provider](images/abp-studio-no-layers-new-solution-dialog-database-provider-efcore-0.9.13.png)
{{ else }}
![abp-studio-new-solution-dialog-database-provider](images/abp-studio-no-layers-new-solution-dialog-database-provider-mongo.png)
![abp-studio-new-solution-dialog-database-provider](images/abp-studio-no-layers-new-solution-dialog-database-provider-mongo-0.9.13.png)
{{ end }}
On that screen, you can decide on your database provider by selecting one of the provided options. There are some additional options for each database provider. Leave them as default or change them based on your preferences, then click the *Next* button for additional *Database Configurations*:
{{ if DB == "EF" }}
![abp-studio-new-solution-dialog-database-configurations](images/abp-studio-no-layers-new-solution-dialog-database-configurations-efcore.png)
![abp-studio-new-solution-dialog-database-configurations](images/abp-studio-no-layers-new-solution-dialog-database-configurations-efcore-0.9.13.png)
{{ else }}
![abp-studio-new-solution-dialog-database-configurations](images/abp-studio-no-layers-new-solution-dialog-database-configurations-mongo.png)
![abp-studio-new-solution-dialog-database-configurations](images/abp-studio-no-layers-new-solution-dialog-database-configurations-mongo-0.9.13.png)
{{ end }}
Here, you can select the database management systems (DBMS){{ if DB == "EF" }} and the connection string{{ end }}. Now, we are ready to allow ABP Studio to create our solution. Just click the *Create* button and let the ABP Studio do the rest for you.
Here, you can select the database management systems (DBMS){{ if DB == "EF" }} and the connection string{{ end }}. Then, you can select optional modules and enable additional options according to your preferences.
![abp-studio-no-layers-new-solution-additional-options](images/abp-studio-no-layers-new-solution-additional-options-0.9.13.png)
Now, we are ready to allow ABP Studio to create our solution. Just click the *Create* button and let the ABP Studio do the rest for you.
After clicking the Create button, the dialog is closed and your solution is loaded into ABP Studio:

BIN
docs/en/images/generic-repositories.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

After

Width:  |  Height:  |  Size: 165 KiB

BIN
docs/en/images/idle-message.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
docs/en/images/idle-setting.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 KiB

BIN
docs/en/images/pen-test-alert-list-9.0.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

1
docs/en/modules/account-pro.md

@ -358,3 +358,4 @@ This module doesn't define any additional distributed event. See the [standard d
* [Impersonation](./account/impersonation.md)
* [Linked Accounts](./account/linkedaccounts.md)
* [Session Management](./account/session-management.md)
* [Idle Session Timeout](./account/idle-session-timeout.md)]

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

@ -0,0 +1,19 @@
# Idle Session Timeout
The `Idle Session Timeout` feature allows you to automatically log out users after a certain period of inactivity.
## Configure Idle Session Timeout
You can enable/disable the `Idle Session Timeout` feature in the `Setting > Account > Idle Session Timeout` page.
The default idle session timeout is 1 hour. You can change it by selecting a different value from the dropdown list or entering a custom value(in minutes).
![idle-setting](../../images/idle-setting.png)
Once the idle session timeout is reached, the user will see a warning modal before being logged out. if user does not respond for 60 seconds, the user will be logged out automatically.
![idle-setting](../../images/idle-message.png)
## How it works
There is JavaScript code running in the background to detect user activity. such as mouse movement, key press, click, etc. If there is no activity detected for setting time, The warning modal will be shown to the user.

46
docs/en/others/penetration-test-report.md

@ -1,6 +1,6 @@
# ABP Penetration Test Report
The ABP Commercial MVC `v8.3.0` application template has been tested against security vulnerabilities by the [OWASP ZAP v2.14.0](https://www.zaproxy.org/) tool. The demo web application was started on the `https://localhost:44349` address. The below alerts have been reported by the pentest tool. These alerts are sorted by the risk level as high, medium, and low. The informational alerts are not mentioned in this document.
The ABP Commercial MVC `v9.0.0` application template has been tested against security vulnerabilities by the [OWASP ZAP v2.14.0](https://www.zaproxy.org/) tool. The demo web application was started on the `https://localhost:44349` address. The below alerts have been reported by the pentest tool. These alerts are sorted by the risk level as high, medium, and low. The informational alerts are not mentioned in this document.
Many of these alerts are **false-positive**, meaning the vulnerability scanner detected these issues, but they are not exploitable. It's clearly explained for each false-positive alert why this alert is a false-positive.
@ -10,14 +10,14 @@ In the next sections, you will find the affected URLs, attack parameters (reques
There are high _(red flag)_, medium _(orange flag)_, low _(yellow flag)_, and informational _(blue flag)_ alerts.
![penetration-test-8.3.0](../images/pen-test-alert-list-8.3.png)
![penetration-test-9.0.0](../images/pen-test-alert-list-9.0.png)
> The informational alerts are not mentioned in this document. These alerts are not raising any risks on your application and they are optional.
### Path Traversal [Risk: High] - False Positive
- *[GET] - https://localhost:44349/api/audit-logging/audit-logs?startTime=&endTime=&url=&userName=&applicationName=&clientIpAddress=&correlationId=&httpMethod=audit-logs&httpStatusCode=&maxExecutionDuration=&minExecutionDuration=&hasException=true&sorting=executionTime+desc&skipCount=0&maxResultCount=10* (attack: **httpMethod=audit-logs**)
- *[POST] - https://localhost:44349/Account/Login* (attack: **\Login**)
- *[POST] - https://localhost:44349/Account/LinkLogin* (attack: **\LinkLogin**)
- *[POST] - https://localhost:44349/Account/Register* (attack: **\Register**)
- *[POST] - https://localhost:44349/Account/SecurityLogs* (attack: **\SecurityLogs**)
- *[POST] - https://localhost:44349/Identity/SecurityLogs* (attack: **\SecurityLogs**)
@ -28,19 +28,15 @@ The Path Traversal attack technique allows an attacker access to files, director
**Solution**:
This is a **false-positive** alert since ABP does all related checks for this kind of attack on the backend side for these endpoints.
This is a **false-positive** alert since ABP does all related checks for this kind of attacks on the backend side for these endpoints.
### SQL Injection [Risk: High] - False Positive
* *[POST] — https://localhost:44349/Account/Login* (attack: **1q2w3E* AND 1=1 --**)
* *[POST] — https://localhost:44349/AuditLogs* (attack: **GET' AND '1'='1' --**)
* *[POST] — https://localhost:44349/Identity/SecurityLogs* (attack: **admin' AND '1'='1**)
* *[POST] — https://localhost:44349/api/account/verify-authenticator-code* (attack: **AND '1'='1**)
* *[POST] — https://localhost:44349/Identity/ClaimTypes/CreateModal* (attack: **aaaa AND '1'='1**)
* *[POST] — https://localhost:44349/Account/ImpersonateUser* (attack: **CfDJ8Pyqeg0vtHtJpnK-9eLaft7-JxLJfJ6WHKPOdBZVxz14BDo061qpJ2NLplgAn2Hw16ec0IR38_wWAUkJGxP8hL6PcLfH0bh-ATNTspWyWYTGGbiH-zeKWiS5vWX-br2BA1hE7Dc45eWGUZNcVc_vm2s AND 1=1 --**)
* *[POST] — https://localhost:44349/Abp/MultiTenancy/TenantSwitchModal* (attack: **CfDJ8Pyqeg0vtHtJpnK-9eLaft7-JxLJfJ6WHKPOdBZVxz14BDo061qpJ2NLplgAn2Hw16ec0IR38_wWAUkJGxP8hL6PcLfH0bh-ATNTspWyWYTGGbiH-zeKWiS5vWX-br2BA1hE7Dc45eWGUZNcVc_vm2s AND 1=1 --**)
* *[POST] — https://localhost:44349/Identity/OrganizationUnits/\** (attack: **6f4cd0ab-f4eb-7ce0-8b26-3a138af1840d" AND '1'='1**) (also, several other URLs...)
* *[POST] — https://localhost:44349/Identity/ClaimTypes/EditModal* (attack: **aaaa AND '1'='1**)
* *[POST] — https://localhost:44349/LanguageManagement/Texts* (attack: **true" AND "1"="1" --**)
* *[POST] — https://localhost:44349/Account/Manage?CurrentPassword=ZAP%27+AND+%271%27%3D%271%27+--+&NewPassword=ZAP&NewPasswordConfirm=ZAP*
* *[POST] — https://localhost:44349/Identity/ClaimTypes/CreateModal* (attack: **aaaad AND '1'='1**)
**Description**:
@ -95,11 +91,17 @@ There are only one URL that is reported as exposing error messages. This is a **
### Content Security Policy (CSP) Header Not Set [Risk: Medium] — Positive (Fixed)
- *[GET] — https://localhost:44349*
- *[GET] — https://localhost:44349/AuditLogs*
- *[GET] — https://localhost:44349/CookiePolicy*
- *[GET] — https://localhost:44349/Gdpr/PersonalData*
- *[GET] — https://localhost:44349/Identity/ClaimTypes/{0}* (create & edit modal URLs - also there are other modal related URLs...)
- *[GET] — https://localhost:44349/AbpPermissionManagement/PermissionManagementModal?providerName=R&providerKey=role&providerKeyDisplayName=role*
- *[GET] — https://localhost:44349/Abp/MultiTenancy/TenantSwitchModal*
- *[GET] — https://localhost:44349/Account/AuthorityDelegation/AuthorityDelegationModal*
- *[GET] — https://localhost:44349/Account/AuthorityDelegation/DelegateNewUserModal*
- *[GET] — https://localhost:44349/Account/ForgotPassword _(other several account URLS)_*
- *[GET] — https://localhost:44349/Account/ExternalLogins _(other several account URLS)_*
- *[GET] — https://localhost:44349/Account/SecurityLogs _(other several account URLS)_*
- *[GET] — https://localhost:44349/Account/Login _(other several account URLS)_*
- *[GET] — https://localhost:44349/Account/Register _(other several account URLS)_*
- *[GET] — https://localhost:44349/Account/Manage _(other several account URLS)_*
@ -143,12 +145,13 @@ The first affected URL is a **false-positive** alert since it's already fixed an
The second URL is also a **false-positive** alert because there is no bad character string in the response.
> **Note**: However, it might be possible if you had any sensitive localization key-value pair in your localization entries, because this endpoint returns all localization values to be able to be used in the application. Therefore, keep that in mind while defining new localization entries.
> **Note**: However, it might be possible if you had any sensitive localization key-value pair in your localization entries, because this endpoint returns all localization values to be able to be used in the application. Therefore, keep that in mind while defining new localization entries. Pass the critical values in your code while using the localization entry as a parameter.
### XSLT Injection [Risk: Medium] - False Positive
- *[GET] — https://localhost:44349/Abp/Languages/Switch?culture=%3Cxsl%3Avalue-of+select%3D%22system-property%28%27xsl%3Avendor%27%29%22%2F%3E&returnUrl=%2F&uiCulture=ar*
- *[POST] — https://localhost:44349/Account/Login _(same URL with different parameters...)_*
- *[POST] — https://localhost:44349/Account/ImpersonateUser _(same URL with different parameters...)_*
- *[POST] — https://localhost:44349/Account/Register _(same URL with different parameters...)_*
- *[POST] — https://localhost:44349/Account/Manage _(same URL with different parameters...)_*
- *[POST] — https://localhost:44349/Account/ForgotPassword _(same URL with different parameters...)_*
@ -161,13 +164,14 @@ Injection using XSL transformations may be possible and may allow an attacker to
**Explanation**:
This is a **false-positive** alert. v8.3.0 uses .NET 8 and the XSLT transformation is not possible on .NET5 or higher.
This is a **false-positive** alert. v9.0 uses .NET 9 and the XSLT transformation is not possible on .NET5 or higher.
### Application Error Disclosure [Risk: Low] — False Positive
- *[GET] — https://localhost:44349/Abp/Languages/Switch*
- *[POST] — https://localhost:44349/Account/ImpersonateUser*
- *[POST] — https://localhost:44349/Saas/Host/Editions*
- *[POST] — https://localhost:44349/Saas/Host/Tenants*
- *[GET] — https://localhost:44349/Account/ExternalLogins*
- *[GET] — https://localhost:44349/Account/Logout*
**Description:**
@ -304,6 +308,7 @@ This vulnerability was reported as a positive alert because the application ran
### Timestamp Disclosure - Unix [Risk: Low] - False Positive
- *[GET] — https://localhost:44349/libs/zxcvbn/zxcvbn.js?=*
- *[GET] — https://localhost:44349/libs/sweetalert2/sweetalert2.all.min.js?=*
**Description**:
@ -319,8 +324,9 @@ This vulnerability was reported as a positive alert, because ABP uses the [zxcvb
### X-Content-Type-Options Header Missing [Risk: Low] - Positive (Fixed)
- *[GET] — https://localhost:44349/client-proxies/account-proxy.js?_v=638550091940000000 (and other client-proxies related URLs)*
- *[GET] — https://localhost:44349/client-proxies/account-proxy.js?_v=638550091940000000 (and other client-proxies related URLs...)*
- *[GET] — https://localhost:44349/favicon.svg*
- *[GET] — https://localhost:44349/images/getting-started/bg-01.png* (and other image URLs...)
- *[GET] — https://localhost:44349/global-styles.css?_v=638556076064360335*
- *[GET] — https://localhost:44349/libs/@fortawesome/fontawesome-free/css/all.css?_v=%5CWEB-INF%5Cweb.xml (other several URLs...)*
- other URLs...
@ -340,11 +346,3 @@ If possible, ensure that the end user uses a standards-compliant and modern web
The `X-Content-Type-Options` header allows you to avoid MIME type sniffing by saying that the MIME types are deliberately configured. This headeer is not strictly required, but it is highly recommended for security reasons. While modern browsers have improved security features, you can still set this header for ensuring the security of web applications.
You can add the [ABP's Security Header Middleware](../framework/ui/mvc-razor-pages/security-headers.md#security-headers-middleware) into the request pipeline to set the `X-Content-Type-Options` as *no-sniff*. Also, this middleware adds other pre-defined security headers to your application, including `X-XSS-Protection`, `X-Frame-Options` and `Content-Security-Policy` (if it's enabled). Read [Security Headers](../framework/ui/mvc-razor-pages/security-headers.md) documentation for more info.
## Other Alerts (Fixed)
The following alerts were reported by the community or our customers in v8.2 and fixed:
* https://github.com/abpframework/abp/issues/19576
* https://github.com/abpframework/abp/issues/19588
* https://github.com/abpframework/abp/issues/19589

2
docs/en/samples/easy-crm.md

@ -35,7 +35,7 @@ When you download and open the zip file, you will see two folders:
* First, follow all the steps above to run the server side and seed the sample data.
* Open a command prompt in the angular folder.
* Run the `yarn` command to install NPM packages (requires the [Yarn](https://yarnpkg.com/) package manager).
* Run the `yarn` command to install NPM packages (requires the [Yarn v1.22+ (not v2)](https://classic.yarnpkg.com/en/docs/install) package manager).
* Run the `yarn start` command to run the Angular application. It will automatically open the `localhost://4200` in your default browser once the application initialized.
### Blazor UI

4
docs/en/studio/installation.md

@ -8,7 +8,7 @@
Before you begin the installation process for ABP Studio, ensure that your system meets the following pre-requirements:
### Node
Make sure [Node.js](https://nodejs.org/en) is installed on your system. If you have not installed Node.js, you can download the `v18.19+` version from the official [Node.js website](https://nodejs.org/en/download/prebuilt-installer).
Make sure [Node.js](https://nodejs.org/en) is installed on your system. If you have not installed Node.js, you can download the `v22+` version from the official [Node.js website](https://nodejs.org/en/download/prebuilt-installer).
### WireGuard (Optional)
ABP Studio needs [WireGuard](https://www.wireguard.com/) for Kubernetes operations. You can find the installation instructions for your specific operating system below:
@ -19,7 +19,7 @@ Installation instructions for your Windows operating system are on the official
**For macOS:**
Installation instructions for your macOS operating system are on the official [WireGuard website](https://www.wireguard.com/install/#macos-homebrew-and-macports-basic-cli-homebrew-userspace-go-homebrew-tools-macports-userspace-go-macports-tools).
### Docker (Optional)
### Docker
ABP Studio needs [Docker](https://www.docker.com/) for [Kubernetes](https://kubernetes.io/) operations. Install Docker by following the guidelines on the official [Docker website](https://docs.docker.com/get-docker/).
## Installation

8
docs/en/studio/release-notes.md

@ -2,6 +2,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.
## 0.9.14 (2024-12-03)
* Refactored `dotnet watch` command in solution runner.
* Added multi-tenancy option for open source startup templates.
* Implemented adding angular library when a new microservice is created.
* Adjusted *Optional Modules* section in solution configurations
* Fixed bugs in the nolayers host project.
## 0.9.13 (2024-11-25)
* Angular - Theme-based Fixes for the Home Page.

2
docs/en/studio/version-mapping.md

@ -4,7 +4,7 @@ This document provides a general overview of the relationship between various ve
| **ABP Studio Version** | **ABP Version of Startup Template** |
|------------------------|---------------------------|
| 0.9.9 to 0.9.13 | 9.0.0 |
| 0.9.9 to 0.9.14 | 9.0.0 |
| 0.9.8 | 8.3.4 |
| 0.9.5 to 0.9.7 | 8.3.3 |
| 0.9.2 to 0.9.4 | 8.3.2 |

BIN
docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-generated-tests.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-navigation-property.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-opening.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-solution-test-projects.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/book-store-suite-solution-explorer.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/bookstore-test-succeed.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/studio-browser-suite.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-author-entity-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-author-entity-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-author-new-entity.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-author-pages-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-selection.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-book-pages-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-book-with-author-create-modal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-bookstore-advanced-filter-section.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-custom-code-result.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-enabling-custom-code.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-end-of-generation-modal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/suite-repository-custom-code.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

BIN
docs/en/tutorials/book-store-with-abp-suite/images/test-data-seed-contributors.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

40
docs/en/tutorials/book-store-with-abp-suite/index.md

@ -0,0 +1,40 @@
# Web Application Development (with ABP Suite) Tutorial
````json
//[doc-params]
{
"UI": ["MVC"],
"DB": ["EF"]
}
````
````json
//[doc-nav]
{
"Next": {
"Name": "Creating the Solution",
"Path": "tutorials/book-store-with-abp-suite/part-01"
}
}
````
> This tutorial is suitable for those who have an ABP Team or a higher [license](https://abp.io/pricing).
## About This Tutorial
> In this tutorial, you will use the [ABP Suite](../../suite/index.md) to generate everything you need to build the **BookStore** application, such as [*Entities*](../../framework/architecture/domain-driven-design/entities.md), [*Domain Services*](../../framework/architecture/domain-driven-design/domain-services.md), [*Application Services*](../../framework/architecture/domain-driven-design/application-services.md), *CRUD pages* and more...
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Value}}** as the database provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts:
- [Part 1: Creating the Solution](part-01.md)
- [Part 2: Creating the Books](part-02.md)
- [Part 3: Creating the Authors](part-03.md)
- [Part 4: Book to Author Relation](part-04.md)
- [Part 5: Customizing the Generated Code](part-05.md)
### Download the Source Code
After logging in to the ABP website, you can download the source code from [here](https://abp.io/api/download/samples/suite-bookstore-mvc-ef).

40
docs/en/tutorials/book-store-with-abp-suite/part-01.md

@ -0,0 +1,40 @@
# Web Application Development (with ABP Suite) Tutorial - Part 1: Creating the Solution
````json
//[doc-params]
{
"UI": ["MVC"],
"DB": ["EF"]
}
````
````json
//[doc-nav]
{
"Next": {
"Name": "Creating the Books",
"Path": "tutorials/book-store-with-abp-suite/part-02"
}
}
````
Before starting the development, create a new solution named `Acme.BookStore` and run it by following the [getting started tutorial](../../get-started/layered-web-application.md).
You can use the following configurations:
* **Solution Name:** `Acme.BookStore`
* **UI Framework:** {{if UI=="MVC"}} ASP.NET Core MVC / Razor Pages {{end}}
* **UI Theme:** LeptonX
* **Mobile Framework:** None
* **Database Provider:** {{if DB=="EF"}} Entity Framework Core {{end}}
You can select the other options based on your preference.
> **Please complete the [Get Started](../../get-started/layered-web-application.md) guide and run the web application before going further.**
The initial solution structure should be like the following in the ABP Studio's [Solution Explorer](../../studio/solution-explorer.md):
![](./images/book-store-suite-solution-explorer.png)
## Summary
We've created the initial layered monolith solution. In the next part, we will learn how to create entities, and generate CRUD pages based on the specified options (including tests, UI, customizable code support etc.) with [ABP Suite](../../suite/index.md).

123
docs/en/tutorials/book-store-with-abp-suite/part-02.md

@ -0,0 +1,123 @@
# Web Application Development (with ABP Suite) Tutorial - Part 2: Creating the Books
````json
//[doc-params]
{
"UI": ["MVC"],
"DB": ["EF"]
}
````
````json
//[doc-nav]
{
"Previous": {
"Name": "Creating the Solution",
"Path": "tutorials/book-store-with-abp-suite/part-01"
},
"Next": {
"Name": "Creating the Authors",
"Path": "tutorials/book-store-with-abp-suite/part-03"
}
}
````
In this part, you will create a new entity named `Book` and generate CRUD pages for the related entities with everything that you would normally implement manually (including application services, tests, CRUD pages, database relations and more...) via [ABP Suite](../../suite/index.md) with few clicks.
## Opening the ABP Suite
> Please **stop the application** in ABP Studio's *Solution Runner* panel, if it's currently running, because ABP Suite will make changes in the solution and it might need to build the solution in some steps and running the solution prevents to build it.
After creating the solution in the previous part, now you can open the ABP Suite and start generating CRUD pages. You can select the *ABP Suite -> Open* command on the main menu to open ABP Suite:
![opening the ABP Suite](./images/abp-suite-opening.png)
After clicking the related command, pre-integrated browser of ABP Studio should open, then you can start generating entities and all related codes with a few configurations:
![](./images/studio-browser-suite.png)
## Creating the Book Entity
Let's create a `Book` entity with some properties. First, type `Book` for the *Name* field and leave the other options as is. ABP Suite automatically calculates proper values for the rest of the inputs for you:
![](./images/suite-book-entity-1.png)
ABP Suite sets:
* Entity type as **master** (ABP Suite allows you to establish [master-child relationship](../../suite/creating-master-detail-relationship.md)),
* Base class as **FullAuditedAggregateRoot** ([see other possible values](../../framework/architecture/domain-driven-design/entities.md)),
* Primary key type as **Guid**,
* Plural name, database name, namespace, page title, menu item and more...
You can change the menu-item value as **book** to show a proper icon in the generated UI, and also enable **code customization**, **creating unit & integration tests**, and other options as you wish:
![](./images/suite-book-entity-2.png)
After, specifying the entity metadata, open the *Properties* tab and create the properties shown in the following figure:
![](./images/suite-book-entity-3.png)
Here the details:
* `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* (we will create the file, in the next section).
* `PublishDate` is a **DateTime** property and **not nullable**.
* `Price` is a **float** property and **required**.
You can leave the other configurations as default.
> ABP Suite allows you to define properties with a great range of options, for example, you can specify the property type as *string*, *int*, *float*, *Guid*, *DateTime*, and even *File* (for file upload) and also you can set any options while defining your properties, such as specifying it as *required*, or *nullable*, setting *max-min length*, *default value* and more...
While defining the properties, you defined a *Type* property with the type of *enum*. ABP Suite asks for an enum path to read the enum file, and set the namespace, and enum name in the generated code.
For that purpose, you should create a `BookType` enum in the `Acme.BookStore.Domain.Shared` project under the **Books** folder as follows:
```csharp
namespace Acme.BookStore.Books;
public enum BookType
{
Undefined,
Adventure,
Biography,
Dystopia,
Fantastic,
Horror,
Science,
ScienceFiction,
Poetry
}
```
Then, you can specify the enum path for the `Type` property in ABP Suite like in the following figure:
![](./images/suite-book-entity-4.png)
> After you select the enum file, ABP Suite automatically sets the namespace and enum name, and lists your enum values in the next section. You can change these values, but for now, you can leave as is.
After that, you can click the **Save and Generate** button to start the code generation process:
![](./images/suite-book-entity-5.png)
ABP Suite will generate the necessary code for you. It generates:
* `Book` entity (also `BookBase` class, which is allow you customizing the generated entity),
* Repository implementation (`EfCoreBookRepository` class),
* `BookManager` domain service,
* Input & Output **DTOs** and **application service** implementations (`IBookAppService` & `BookAppService`),
* **Unit & integration tests**,
* A new **migration** (and also applies to the database),
* All related **permission**, **object mapping** and **navigation menu item** configurations,
* and all required **UI components and pages**.
It will take some time to complete the process. After the process is completed, you will see a success message, you can click the *Ok* button, and build & start the application by clicking the *Run -> Build & Start* button in the *Solution Runner* panel:
![](./images/suite-book-entity-6.png)
After the application is started, you can right-click and *Browse* on the application to open it in the ABP Studio's pre-integrated browser. You can see the Books page in the following figure with a single record:
![](./images/suite-book-pages-1.png)
On this page, you can create a new book, update an existing book, delete a book, export all records (or the filtered records) to excel, filter the records by using the advanced filter section, bulk delete multiple records and so on.
## Summary
In this part, you've created a new entity named `Book` and generated the necessary code for it with [ABP Suite](../../suite/index.md) with a few clicks. ABP Suite generated the all code for you, including the **entity**, **application service**, **database relations**, **unit & integration tests**, **UI** and **defined the custom hooks for code customization**.

79
docs/en/tutorials/book-store-with-abp-suite/part-03.md

@ -0,0 +1,79 @@
# Web Application Development (with ABP Suite) Tutorial - Part 3: Creating the Authors
````json
//[doc-params]
{
"UI": ["MVC"],
"DB": ["EF"]
}
````
````json
//[doc-nav]
{
"Previous": {
"Name": "Creating the Books",
"Path": "tutorials/book-store-with-abp-suite/part-02"
},
"Next": {
"Name": "Book to Author Relation",
"Path": "tutorials/book-store-with-abp-suite/part-04"
}
}
````
In the previous part, you have created the `Book` entity and in this part, you will create a new entity named `Author` and generate all necesssary code via [ABP Suite](../../suite/index.md) with few clicks. After creating the `Author` entity, you will be establishing [one-to-many relationship](../../suite/generating-crud-page.md#step-by-step-creating-a-navigation-property-with-1-to-many-relationship) with *Book* and *Author* entities, in the next part.
## Creating the Author Entity
After generating the all necessary code for the `Book` entity, and testing the *Books* page, by building & starting the application, now you can continue with creating the `Author` entity.
> Before, creating the `Author` entity, please **stop the application** in ABP Studio's *Solution Runner* panel, because ABP Suite will make changes in the solution and it might need to build the solution in some steps and running the solution prevents to build it.
Click the entity selection box in the top right of the *CRUD page generation* page, and select the *-New entity-*:
![](/images/suite-author-new-entity.png)
Then, you can type `Author` for the *Name* field and leave the other options as is (you can change the menu icon as **pen** for a proper menu icon, and/or other options, if you wish). ABP Suite automatically calculates proper values for the rest of the inputs for you:
![](/images/suite-author-entity-1.png)
ABP Suite sets:
* Entity type as **master** (ABP Suite allows you to establish [master-child relationship](../../suite/creating-master-detail-relationship.md)),
* Base class as **FullAuditedAggregateRoot** ([see other possible values](../../framework/architecture/domain-driven-design/entities.md)),
* Primary key type as **Guid**,
* Plural name, database name, namespace, page title, menu item and more...
* Also, it enables **code customization**, **UI code generation**, **unit & integration test generation** and **bulk delete** by default.
After, specifying the entity metadata, open the *Properties* tab and create the properties shown in the following figure:
![](./images/suite-author-entity-2.png)
Here the details:
* `Name` is **required**, it's a **string** property, and it's a **textarea**. Minimum length is **2** and maximum length is **128**.
* `BirthDate` is a **DateTime** property and **not nullable**.
* `ShortBio` is a **string** property, it's **required**, maximum length is **256**.
You can leave the other configurations as default.
> **Note:** All properties are marked as **filterable** by default, and they appear in the advanced filter section because of that. You can set any properties you want as **not filterable** and then the related property will be removed from the advanced filter section and code will be generated accordingly.
You can click the **Save and Generate** button to start the code generation process:
![](./images/suite-book-entity-5.png)
ABP Suite will generate the necessary code for you. It will take some time to complete the process. After the process is completed, you will see a success message, you can click the *Ok* button, and build & start the application by clicking the *Run -> Build & Start* button in the *Solution Runner* panel:
![](./images/suite-book-entity-6.png)
After the application is started, you can right-click and *Browse* on the application to open it in the ABP Studio's pre-integrated browser and try to add a new author:
![](./images/suite-author-pages-1.png)
As you can see, the *Name* field is **required**, the *Birth Date* field shows a datepicker, and the *Short Bio* field is also **required** and it's a **textarea**. You just configured how you want your properties with some options (for example, setting the short bio as **textarea** and **required**), and ABP Suite generated code according that.
## Summary
In this part, you've created a new entity named `Author` and generated the necessary code for it with [ABP Suite](../../suite/index.md) with a few clicks. ABP Suite generated the all code for you, including the **entity**, **application service**, **database relations**, **unit & integration tests**, **UI** and **defined the custom hooks for code customization**.
In the next part, you will establish [one-to-many relation](../../suite/generating-crud-page.md) between the `Book` and `Author` entities.

168
docs/en/tutorials/book-store-with-abp-suite/part-04.md

@ -0,0 +1,168 @@
# Web Application Development (with ABP Suite) Tutorial - Part 4: Book to Author Relation
````json
//[doc-params]
{
"UI": ["MVC"],
"DB": ["EF"]
}
````
````json
//[doc-nav]
{
"Previous": {
"Name": "Creating the Author",
"Path": "tutorials/book-store-with-abp-suite/part-03"
},
"Next": {
"Name": "Customizing the Generated Code",
"Path": "tutorials/book-store-with-abp-suite/part-05"
}
}
````
In the previous parts, you have created the `Book` and `Author` entities (& generated code for all functionalities) for the book store application. However, currently there is no relation between these entities.
In this part, you will establish to **one-to-many relation** between the `Book` and `Author` entities.
## Establishing Relations with ABP Suite
ABP Suite allows establishing both **one-to-many** and [many-to-many](../../suite/creating-many-to-many-relationship.md) relationships.
In this tutorial, you will only establish **one-to-many relation** between `Book` and `Author` entities. It's pretty straightforward to establish a relationship with ABP Suite. You should just need to navigate to the *Navigations* tab, and provide the metadata for navigation property (1-n) or navigation collection (n-n) relations.
## Creating Book to Author Relationship
> Please **stop the application** in ABP Studio's *Solution Runner* panel, because ABP Suite will make changes in the solution and it might need to build the solution in some steps and running the solution prevents to build it.
To establish **one-to-many relations** between *Book* and *Author* entities, select the `Book` entity from the entity selection box on the top-right of the *CRUD page generation* page:
![](./images/suite-book-entity-selection.png)
Then, you can open the *Navigations* tab, and click the **Add navigation property (1-n)** button. After that, a navigation property model will open, and you can fill the fields like in the following figure:
![](./images/abp-suite-navigation-property.png)
Here is the details:
* Selected the entity as `Author`. (ABP Suite will establish one-to-many relation between *Book* and *Author* entities with this configuration)
* Set the property name as *AuthorId*, it will be set as foreign-key restriction in the database and all related database configurations will be made by ABP Suite.
* Selected the display property as *Name*, this will be used in the dropdown component to set an author with a book & also it will be shown in the datatable of the *Books* page.
* Also, made the relation **required** and set it **filterable** so books can be filterable by authors.
After, specifying the metadata, you can click the *Ok* button to close the modal. Then, click the **Save and generate** button to start code generation process. ABP Suite will establish one-to-many relationship between the entities, and will generate all necessary code automatically:
![](./images/suite-end-of-generation-modal.png)
It will take some time to complete the process. After the process is completed, you will see a success message, you can click the *Ok* button, and build & start the application by clicking the *Run -> Build & Start* button in the *Solution Runner* panel:
![](./images/suite-book-entity-6.png)
After the application is started, you can right-click and *Browse* on the application to open it in the ABP Studio's pre-integrated browser. You can first create an author and then create a book with the author for testing:
![](./images/suite-book-with-author-create-modal.png)
Also, notice that, in the advanced filter section, there is an **Author** dropdown, which you can use to filter books by authors (remember you set **filterable** while defining navigation property and thanks to that, ABP Suite generated the code accordingly):
![](./images/suite-bookstore-advanced-filter-section.png)
## Unit & Integration Tests
Since you completed the bookstore application, now we can check the generated tests, and run them to see if all of them pass or not.
There are several test projects in the solution:
![](./images/abp-suite-solution-test-projects.png)
> Test projects slightly differs based on your UI and Database selection. For example, if you select MongoDB, then the `Acme.BookStore.EntityFrameworkCore.Tests` will be `Acme.BookStore.MongoDB.Tests`.
ABP Suite generated unit & integration tests, for the `Book` & `Author` entities. If you open the **Test explorer** in your IDE, you will see the following tests are generated:
![](./images/abp-suite-generated-tests.png)
ABP Suite generated tests for repository implementations & application service implementations for the generated code, if you enable *Create unit & integration tests* option, while creating the entity. Since, you already did that in the previous parts, it generated the all required tests for the entities.
Let's examine one of the generated test classes. Open the *BooksAppServiceTests* (under the *test/Acme.BookStore.Application.Tests/Books/BookApplicationTests.cs*) and check the `CreateAsync` method:
```csharp
[Fact]
public async Task CreateAsync()
{
// Arrange
var input = new BookCreateDto
{
Name = "6c3d1eda8bf04852b7bd5dfdbbd93224b252478c2e474d4c8faf24fa6b182168ca830d4f80e64e4a8e363f33e151d1d34a04be4709274c7fbf2214f9bb3a16c3",
Type = default,
PublishDate = new DateTime(2006, 8, 21),
Price = 754882891,
AuthorId = Guid.Parse("602460f6-df6e-456a-89d9-8c5870dfc583")
};
// Act
var serviceResult = await _booksAppService.CreateAsync(input);
// Assert
var result = await _bookRepository.FindAsync(c => c.Id == serviceResult.Id);
result.ShouldNotBe(null);
result.Name.ShouldBe("6c3d1eda8bf04852b7bd5dfdbbd93224b252478c2e474d4c8faf24fa6b182168ca830d4f80e64e4a8e363f33e151d1d34a04be4709274c7fbf2214f9bb3a16c3");
result.Type.ShouldBe(default);
result.PublishDate.ShouldBe(new DateTime(2006, 8, 21));
result.Price.ShouldBe(754882891);
}
```
ABP Suite;
* Create the `BookCreateDto` input DTO object, and fill its values with dummy data to simulate creating a book,
* Then, it calls the `IBooksAppService.CreateAsync` method to create a book,
* And finally, asserts the returned result to see if it's as expected or not.
Notice, also the *AuthorId* is set in the `BookCreateDto` object. At that point, you might ask yourself that I haven't created the author with that ID before, should not it throw exception?
No, it will not throw an exception, because ABP Suite also generates simple dummy data for the entities just for the tests! You can see the test data seed contributors under the *Acme.BookStore.Domain.Tests* project:
![](./images/test-data-seed-contributors.png)
Here is the content of the `AuthorsDataSeedContributor.SeedAsync` method:
```csharp
public async Task SeedAsync(DataSeedContext context)
{
if (IsSeeded)
{
return;
}
await _authorRepository.InsertAsync(new Author
(
id: Guid.Parse("602460f6-df6e-456a-89d9-8c5870dfc583"),
name: "d7bbb3bff0d54ad799477298c4572e9c05fd1175ab21416da17d0001e2b697cd7fef99fdb4414f26a05789667a97442bd65865510ba34c3599e874ccf08b45e4",
birthDate: new DateTime(2010, 2, 11),
shortBio: "3c2ff43c18e34d7b9ad3f1b9c444cbb000f90808d3774cb6b7702b957f472d74048597f93df744f6a6fdf507be428e016edec982f1174e09b124982cbc40156290ce6bc9fd7b49b4972741956cc847891cb55ad0942f4534b90aa0561d3e0c200340b613c7ad40c38b4b2f2c39298169a853473faed34341a130b31e1eb57e92"
));
await _authorRepository.InsertAsync(new Author
(
id: Guid.Parse("6ea5a6b2-919e-4334-9728-13f4872e5e0e"),
name: "fd332fb58f184716962b08fbaa92f1c3e0963d843ba34c82bb5409517f60da3727c43b05e8d4490f996c5d19265962e53a69ed5e3e144509aad1441e37ce5081",
birthDate: new DateTime(2010, 6, 10),
shortBio: "b7808946c46c42e3935c4d8203d82973cfb98c5d81644f1da4ce1e643767849e23e0eb12a92f48be8f7eec0c07aefa043721fdd3fea542cfa644d2b7d428dc8842647180ef8a47139e097f6674c4f0d86c46765c406042a2a858865cb112ecd78d9ef6f5843e444994641f924a38a2d24ee4e212d41444888d3c0861af0cf9dd"
));
await _unitOfWorkManager!.Current!.SaveChangesAsync();
IsSeeded = true;
}
```
Since ABP Suite generated the test data seed contributors for each entity, you have initial data while testing your services. Also, as you would notice, the id in this example (*602460f6-df6e-456a-89d9-8c5870dfc583*) is same as the *authorId* field in the `BooksAppServiceTests.CreateAsync` method.
Let's execute all tests, and see the results:
![](./images/bookstore-test-succeed.png)
## Summary
So far, you have created the all functionality for the bookstore application without needing to write any single line of code. ABP Suite generated the entities, application services, UI components, unit & integration tests and more...
In the next part, you will write some code and modify the ABP Suite's generated code by writing the code in the specified hookpoints. Thanks to [ABP Suite's Customized Code Support](../../suite/customizing-the-generated-code.md), in the next generation, our custom code will not be overridden and will be preserved.

123
docs/en/tutorials/book-store-with-abp-suite/part-05.md

@ -0,0 +1,123 @@
# Web Application Development (with ABP Suite) Tutorial - Part 5: Customizing the Generated Code
````json
//[doc-params]
{
"UI": ["MVC"],
"DB": ["EF"]
}
````
````json
//[doc-nav]
{
"Previous": {
"Name": "Book to Author Relation",
"Path": "tutorials/book-store-with-abp-suite/part-04"
}
}
````
So far, you have created the all functionality for the bookstore application without needing to write any single line of code. In this part, let's write some code and check one of the great features of the ABP Suite, which is [Customizable Code Support](../../suite/customizing-the-generated-code.md).
## Customizable Code Support
ABP Suite allows you to customize the generated code blocks and preserve your custom code changes in the next CRUD Page Generation. It specifies hook points to allow adding custom code blocks. Then, the code written by you to these hook points will be respected and will not be overridden in the next CRUD Page Generation.
To enable custom code support, you should check the *Customizable code* option in the **CRUD Page Generation** page (it's selected by default), and you enabled it for both entities:
![](./images/suite-enabling-custom-code.png)
## Custom Code Hookpoints
When you enable the *custom code support*, ABP Suite adds some hookpoints that you can write your own custom code without worrying about, are my codes being overridden with the next CRUD page generation.
On the C# side, ABP Suite adds abstract base classes for entities, application services, interfaces, domain services and so on... (and partial classes for interfaces)
You can write your custom code in those classes (with the `*.Extended.cs` extension) and next time when you need to re-generate the entity, your custom code will not be overridden (only the base abstract classes will be re-generated and your changes on Suite will be respected):
![](./images/suite-repository-custom-code.png)
> For example, you can create a new repository method like in the example above, and in the next CRUD page generation, you will ABP Suite won't override your custom code.
On the UI side, ABP Suite provides convenient comment placeholders within pages for MVC, Blazor, and Angular UIs. These comment sections serve as hook points where you can add your custom code.
For example, if you open the *Books/Index.cshtml* file in your IDE, you will see those placeholders like following:
```xml
<!-- Code omitted for brevity -->
@section styles
{
@*//<suite-custom-code-block-1>*@
@*//</suite-custom-code-block-1>*@
}
<!-- ... -->
```
You can write your custom codes between the _**<suite-custom-code-block-n></suite-custom-code-block-n>**_ placeholders and you can also extend these placeholders by customizing the [ABP Suite templates](../../suite/editing-templates.md).
> For more information, please refer to [Customizing the Generated Code documentation](../../suite/customizing-the-generated-code.md)
## Implementing Custom Code
Let's see the custom code support in action. We can demonstrate this feature with an easy example.
Assume that we want to show the author's name with his abbreviated name. For example, for the author *John Ronald Reuel Tolkien*, we want to show the name *John Ronald Reuel Tolkien (a.k.a J.R.R.T)*. Achieving that is pretty straightforward.
We just need to open the *src/Acme.BookStore.Application/Books/BooksAppService.Extended.cs* file and override the base `GetListAsync` method, which is called on the books page:
```csharp
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Caching;
namespace Acme.BookStore.Books
{
public class BooksAppService : BooksAppServiceBase, IBooksAppService
{
//<suite-custom-code-autogenerated>
public BooksAppService(IBookRepository bookRepository, BookManager bookManager, IDistributedCache<BookDownloadTokenCacheItem, string> downloadTokenCache, IRepository<Acme.BookStore.Authors.Author, Guid> authorRepository)
: base(bookRepository, bookManager, downloadTokenCache, authorRepository)
{
}
//</suite-custom-code-autogenerated>
//Write your custom code...
public override async Task<PagedResultDto<BookWithNavigationPropertiesDto>> GetListAsync(GetBooksInput input)
{
var result = await base.GetListAsync(input);
foreach (var book in result.Items)
{
var akaName = book.Author.Name.Split(" ").Select(q => q[0]).JoinAsString(".");
book.Author.Name += $" (a.k.a {akaName})";
}
return result;
}
}
}
```
* Here, we have overridden the `GetListAsync` method and changed its result according to our need.
* Notice, this class is derieved from the `BooksAppServiceBase` class and implements the `IBooksAppService`.
* Thus, ABP Suite only modifies the `BooksAppServiceBase` class and implements the necessary services in each generation, but does not generate the files with the `*.Extended` postfixes and let you implement your own custom code.
* You can create new methods, override an existing method and change its behaviour, add custom hookpoints to extend customization capabilities and more...
Now, we can open ABP Suite and try to regenerate the book entity and see if our custom code is gone or not:
![](./images/suite-end-of-generation-modal.png)
After the regeneration has been completed, we can check the **BooksAppService.Extended.cs** file and we should see our custom code is there without any modification. ABP Suite didn't override our custom code, and finally, we can run the application to see the final result:
![](./images/suite-custom-code-result.png)
ABP Suite's custom code support is not limited to the backend side. You can also override the UI. You can refer to the [Customizing the Generated Code document](../../suite/customizing-the-generated-code.md) for further info.
## Summary
In this tutorial, you created the bookstore application without needing to write a single line of code with ABP Suite. Then, in this last part, you have written custom code in the specified hookpoints and it did not override by ABP Suite.

2
docs/en/tutorials/book-store/part-01.md

@ -34,7 +34,7 @@ For such cases, run the `abp install-libs` command on the root directory of your
abp install-libs
```
> We suggest you install [Yarn](https://classic.yarnpkg.com/) to prevent possible package inconsistencies, if you haven't installed it yet.
> We suggest you install [Yarn v1.22+ (not v2)](https://classic.yarnpkg.com/en/docs/install) to prevent possible package inconsistencies, if you haven't installed it yet.
{{if UI=="Blazor" || UI=="BlazorWebApp"}}

6
docs/en/tutorials/todo/layered/index.md

@ -52,8 +52,8 @@ This documentation has a video tutorial on **YouTube**!! You can watch it here:
## Pre-Requirements
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 8.0+](https://dotnet.microsoft.com/download/dotnet) development.
* [Node v18.19+](https://nodejs.org/)
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 9.0+](https://dotnet.microsoft.com/download/dotnet) development.
* [Node v20.11+](https://nodejs.org/)
{{if DB=="Mongo"}}
@ -111,7 +111,7 @@ For such cases, run the `abp install-libs` command on the root directory of your
abp install-libs
````
> We suggest you install [Yarn](https://classic.yarnpkg.com/) to prevent possible package inconsistencies, if you haven't installed it yet.
> We suggest you install [Yarn v1.22+ (not v2)](https://classic.yarnpkg.com/en/docs/install) to prevent possible package inconsistencies, if you haven't installed it yet.
{{if UI=="Blazor" || UI=="BlazorWebApp"}}

6
docs/en/tutorials/todo/single-layer/index.md

@ -48,8 +48,8 @@ This documentation has a video tutorial on **YouTube**!! You can watch it here:
## Pre-Requirements
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 8.0+](https://dotnet.microsoft.com/download/dotnet) development.
* [Node v18.19+](https://nodejs.org/)
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 9.0+](https://dotnet.microsoft.com/download/dotnet) development.
* [Node v20.11+](https://nodejs.org/)
{{if DB=="Mongo"}}
@ -113,7 +113,7 @@ Run the `abp install-libs` command on the root directory of your solution to ins
abp install-libs
```
> We suggest you install [Yarn](https://classic.yarnpkg.com/) to prevent possible package inconsistencies, if you haven't installed it yet.
> We suggest you install [Yarn v1.22+ (not v2)](https://classic.yarnpkg.com/en/docs/install) to prevent possible package inconsistencies, if you haven't installed it yet.
{{if UI=="Blazor" || UI=="BlazorServer"}}

32
docs/en/ui-themes/lepton-x/blazor.md

@ -262,7 +262,7 @@ If you need to replace the component, you can follow the steps below.
* Create a razor page, like `MyBreadcrumbs.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.Common;
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.Common;
@using Volo.Abp.DependencyInjection
@inherits Breadcrumbs
@ -295,7 +295,7 @@ namespace LeptonXLite.DemoApp.Blazor.MyComponents
* Create a razor page, like `MyContentToolbar.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.Common;
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.Common;
@using Volo.Abp.DependencyInjection
@inherits ContentToolbar
@ -328,7 +328,7 @@ namespace LeptonXLite.DemoApp.Blazor.MyComponents
* Create a razor page, like `MyGeneralSettings.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.Common;
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.Common;
@using Volo.Abp.DependencyInjection
@inherits GeneralSettings
@ -361,7 +361,7 @@ namespace LeptonXLite.DemoApp.Blazor.MyComponents
* Create a razor page, like `MyMobileGeneralSettings.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.Common;
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.Common;
@using Volo.Abp.DependencyInjection
@inherits MobileGeneralSettings
@ -400,7 +400,7 @@ Components used in the side menu layout.
* Create a razor page, like `MyMainMenu.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.Navigation;
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.Navigation;
@using Volo.Abp.DependencyInjection
@inherits MainMenu
@ -431,7 +431,7 @@ namespace LeptonXLite.DemoApp.Blazor.MyComponents
* Create a razor page, like `MyMainMenuItem.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu.Navigation;
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu.Navigation;
@using Volo.Abp.DependencyInjection
@inherits MainMenuItem
@ -464,7 +464,7 @@ namespace LeptonXLite.DemoApp.Blazor.MyComponents
* Create a razor page, like `MyMobileNavbar.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu.Navigation;
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu.Navigation;
@using Volo.Abp.DependencyInjection
@inherits MobileNavbar
@ -497,7 +497,7 @@ namespace LeptonXLite.DemoApp.Blazor.MyComponents
* Create a razor page, like `MyMainHeader.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu.MainHeader
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu.MainHeader
@using Volo.Abp.DependencyInjection
@inherits MainHeader
@ -534,7 +534,7 @@ If you need to replace the component, you can follow the steps below.
* Create a razor page, like `MyMainHeaderBranding.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu.MainHeader
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu.MainHeader
@using Volo.Abp.DependencyInjection
@inherits MainHeaderBranding
@ -571,7 +571,7 @@ If you need to replace the component, you can follow the steps below.
* Create a razor page, like `MyMainHeaderToolbar.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu.MainHeader
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.SideMenu.MainHeader
@using Volo.Abp.DependencyInjection
@inherits MainHeaderToolbar
@ -610,7 +610,7 @@ Components used in the top menu layout.
* Create a razor page, like `MyMainMenu.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.Navigation;
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.Navigation;
@using Volo.Abp.DependencyInjection
@inherits MainMenu
@ -641,7 +641,7 @@ namespace LeptonXLite.DemoApp.Blazor.MyComponents
* Create a razor page, like `MyMainMenuItem.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.Navigation;
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.Navigation;
@using Volo.Abp.DependencyInjection
@inherits MainMenuItem
@ -674,7 +674,7 @@ namespace LeptonXLite.DemoApp.Blazor.MyComponents
* Create a razor page, like `MyMobileNavbar.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.Navigation;
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.Navigation;
@using Volo.Abp.DependencyInjection
@inherits MobileNavbar
@ -707,7 +707,7 @@ namespace LeptonXLite.DemoApp.Blazor.MyComponents
* Create a razor page, like `MyMainHeader.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.MainHeader
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.MainHeader
@using Volo.Abp.DependencyInjection
@inherits MainHeader
@ -742,7 +742,7 @@ Application branding can be customized with the `IBrandingProvider`. See the [Br
* Create a razor page, like `MyMainHeaderBranding.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.MainHeader
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.MainHeader
@using Volo.Abp.DependencyInjection
@inherits MainHeaderBranding
@ -779,7 +779,7 @@ If you need to replace the component, you can follow the steps below.
* Create a razor page, like `MyMainHeaderToolbar.razor`, in your blazor application as shown below:
```html
@Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.MainHeader
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.TopMenu.MainHeader
@using Volo.Abp.DependencyInjection
@inherits MainHeaderToolbar

2
docs/en/ui-themes/lepton-x/index.md

@ -88,6 +88,8 @@ You can use the following CLI command to download the source-code:
abp get-source Volo.Abp.LeptonXTheme
```
> If you are using the old ABP CLI, you can use the command: `abp get-source Volo.Abp.LeptonXTheme.Pro`
If you want to download the source code of the preview version, you can use the following command:
```bash

296
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/DaprAspNetCore/AbpDaprEndpointRouteBuilderExtensions.cs

@ -1,296 +0,0 @@
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Dapr
{
/// <summary>
/// This class defines configurations for the subscribe endpoint.
/// </summary>
public class AbpSubscribeOptions
{
/// <summary>
/// Gets or Sets a value which indicates whether to enable or disable processing raw messages.
/// </summary>
public bool EnableRawPayload { get; set; }
/// <summary>
/// An optional delegate used to configure the subscriptions.
/// </summary>
public Func<List<AbpSubscription>, Task>? SubscriptionsCallback { get; set; }
}
/// <summary>
/// This class defines subscribe endpoint response
/// </summary>
public class AbpSubscription
{
/// <summary>
/// Gets or sets the topic name.
/// </summary>
public string Topic { get; set; } = default!;
/// <summary>
/// Gets or sets the pubsub name
/// </summary>
public string PubsubName { get; set; } = default!;
/// <summary>
/// Gets or sets the route
/// </summary>
public string? Route { get; set; }
/// <summary>
/// Gets or sets the routes
/// </summary>
public AbpRoutes? Routes { get; set; }
/// <summary>
/// Gets or sets the metadata.
/// </summary>
public AbpMetadata? Metadata { get; set; }
/// <summary>
/// Gets or sets the deadletter topic.
/// </summary>
public string? DeadLetterTopic { get; set; }
}
/// <summary>
/// This class defines the metadata for subscribe endpoint.
/// </summary>
public class AbpMetadata : Dictionary<string, string>
{
/// <summary>
/// Initializes a new instance of the Metadata class.
/// </summary>
public AbpMetadata() { }
/// <summary>
/// Initializes a new instance of the Metadata class.
/// </summary>
/// <param name="dictionary"></param>
public AbpMetadata(IDictionary<string, string> dictionary) : base(dictionary) { }
/// <summary>
/// RawPayload key
/// </summary>
internal const string RawPayload = "rawPayload";
}
/// <summary>
/// This class defines the routes for subscribe endpoint.
/// </summary>
public class AbpRoutes
{
/// <summary>
/// Gets or sets the default route
/// </summary>
public string? Default { get; set; }
/// <summary>
/// Gets or sets the routing rules
/// </summary>
public List<AbpRule>? Rules { get; set; }
}
/// <summary>
/// This class defines the rule for subscribe endpoint.
/// </summary>
public class AbpRule
{
/// <summary>
/// Gets or sets the CEL expression to match this route.
/// </summary>
public string Match { get; set; } = default!;
/// <summary>
/// Gets or sets the path of the route.
/// </summary>
public string Path { get; set; } = default!;
}
}
namespace Microsoft.AspNetCore.Builder
{
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Dapr;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
/// <summary>
/// Contains extension methods for <see cref="IEndpointRouteBuilder" />.
/// </summary>
public static class AbpDaprEndpointRouteBuilderExtensions
{
/// <summary>
/// Maps an endpoint that will respond to requests to <c>/dapr/subscribe</c> from the
/// Dapr runtime.
/// </summary>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder" />.</param>
/// <returns>The <see cref="IEndpointConventionBuilder" />.</returns>
public static IEndpointConventionBuilder MapAbpSubscribeHandler(this IEndpointRouteBuilder endpoints)
{
return CreateSubscribeEndPoint(endpoints);
}
/// <summary>
/// Maps an endpoint that will respond to requests to <c>/dapr/subscribe</c> from the
/// Dapr runtime.
/// </summary>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder" />.</param>
/// <param name="options">Configuration options</param>
/// <returns>The <see cref="IEndpointConventionBuilder" />.</returns>
/// <seealso cref="MapAbpSubscribeHandler(IEndpointRouteBuilder)"/>
public static IEndpointConventionBuilder MapAbpSubscribeHandler(this IEndpointRouteBuilder endpoints, AbpSubscribeOptions options)
{
return CreateSubscribeEndPoint(endpoints, options);
}
private static IEndpointConventionBuilder CreateSubscribeEndPoint(IEndpointRouteBuilder endpoints, AbpSubscribeOptions? options = null)
{
if (endpoints is null)
{
throw new System.ArgumentNullException(nameof(endpoints));
}
return endpoints.MapGet("dapr/subscribe", async context =>
{
var logger = context.RequestServices.GetService<ILoggerFactory>()?.CreateLogger("DaprTopicSubscription");
var dataSource = context.RequestServices.GetRequiredService<EndpointDataSource>();
var subscriptions = dataSource.Endpoints
.OfType<RouteEndpoint>()
.Where(e => e.Metadata.GetOrderedMetadata<ITopicMetadata>().Any(t => t.Name != null)) // only endpoints which have TopicAttribute with not null Name.
.SelectMany(e =>
{
var topicMetadata = e.Metadata.GetOrderedMetadata<ITopicMetadata>();
var originalTopicMetadata = e.Metadata.GetOrderedMetadata<IOriginalTopicMetadata>();
var subs = new List<(string PubsubName, string Name, string? DeadLetterTopic, bool? EnableRawPayload, string Match, int Priority, Dictionary<string, string[]> OriginalTopicMetadata, string? MetadataSeparator, RoutePattern RoutePattern)>();
for (int i = 0; i < topicMetadata.Count(); i++)
{
subs.Add((topicMetadata[i].PubsubName,
topicMetadata[i].Name,
(topicMetadata[i] as IDeadLetterTopicMetadata)?.DeadLetterTopic,
(topicMetadata[i] as IRawTopicMetadata)?.EnableRawPayload,
topicMetadata[i].Match,
topicMetadata[i].Priority,
originalTopicMetadata.Where(m => (topicMetadata[i] as IOwnedOriginalTopicMetadata)?.OwnedMetadatas?.Any(o => o.Equals(m.Id)) == true || string.IsNullOrEmpty(m.Id))
.GroupBy(c => c.Name)
.ToDictionary(m => m.Key, m => m.Select(c => c.Value).Distinct().ToArray()),
(topicMetadata[i] as IOwnedOriginalTopicMetadata)?.MetadataSeparator,
e.RoutePattern));
}
return subs;
})
.Distinct()
.GroupBy(e => new { e.PubsubName, e.Name })
.Select(e => e.OrderBy(e => e.Priority))
.Select(e =>
{
var first = e.First();
var rawPayload = e.Any(e => e.EnableRawPayload.GetValueOrDefault());
var metadataSeparator = e.FirstOrDefault(e => !string.IsNullOrEmpty(e.MetadataSeparator)).MetadataSeparator?.ToString() ?? ",";
var rules = e.Where(e => !string.IsNullOrEmpty(e.Match)).ToList();
var defaultRoutes = e.Where(e => string.IsNullOrEmpty(e.Match)).Select(e => RoutePatternToString(e.RoutePattern)).ToList();
var defaultRoute = defaultRoutes.FirstOrDefault();
//multiple identical names. use comma separation.
var metadata = new AbpMetadata(e.SelectMany(c => c.OriginalTopicMetadata).GroupBy(c => c.Key).ToDictionary(c => c.Key, c => string.Join(metadataSeparator, c.SelectMany(c => c.Value).Distinct())));
if (rawPayload || options?.EnableRawPayload is true)
{
metadata.Add(AbpMetadata.RawPayload, "true");
}
if (logger != null)
{
if (defaultRoutes.Count > 1)
{
logger.LogError("A default subscription to topic {name} on pubsub {pubsub} already exists.", first.Name, first.PubsubName);
}
var duplicatePriorities = rules.GroupBy(e => e.Priority)
.Where(g => g.Count() > 1)
.ToDictionary(x => x.Key, y => y.Count());
foreach (var entry in duplicatePriorities)
{
logger.LogError("A subscription to topic {name} on pubsub {pubsub} has duplicate priorities for {priority}: found {count} occurrences.", first.Name, first.PubsubName, entry.Key, entry.Value);
}
}
var subscription = new AbpSubscription
{
Topic = first.Name,
PubsubName = first.PubsubName,
Metadata = metadata.Count > 0 ? metadata : null,
};
if (first.DeadLetterTopic != null)
{
subscription.DeadLetterTopic = first.DeadLetterTopic;
}
// Use the V2 routing rules structure
if (rules.Count > 0)
{
subscription.Routes = new AbpRoutes
{
Rules = rules.Select(e => new AbpRule
{
Match = e.Match,
Path = RoutePatternToString(e.RoutePattern),
}).ToList(),
Default = defaultRoute,
};
}
// Use the V1 structure for backward compatibility.
else
{
subscription.Route = defaultRoute;
}
return subscription;
})
.OrderBy(e => (e.PubsubName, e.Topic))
.ToList();
await options?.SubscriptionsCallback!(subscriptions)!;
await context.Response.WriteAsync(JsonSerializer.Serialize(subscriptions,
new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
}));
});
}
private static string RoutePatternToString(RoutePattern routePattern)
{
return string.Join("/", routePattern.PathSegments
.Select(segment => string.Concat(segment.Parts.Cast<RoutePatternLiteralPart>()
.Select(part => part.Content))));
}
}
}

135
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusModule.cs

@ -1,11 +1,16 @@
using System.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Dapr;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Dapr;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Dapr;
using Volo.Abp.EventBus.Distributed;
@ -21,50 +26,98 @@ public class AbpAspNetCoreMvcDaprEventBusModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var subscribeOptions = context.Services.ExecutePreConfiguredActions<AbpSubscribeOptions>();
Configure<AbpEndpointRouterOptions>(options =>
PostConfigure<AbpEndpointRouterOptions>(options =>
{
options.EndpointConfigureActions.Add(endpointContext =>
{
var rootServiceProvider = endpointContext.ScopeServiceProvider.GetRequiredService<IRootServiceProvider>();
subscribeOptions.SubscriptionsCallback = subscriptions =>
{
var daprEventBusOptions = rootServiceProvider.GetRequiredService<IOptions<AbpDaprEventBusOptions>>().Value;
foreach (var handler in rootServiceProvider.GetRequiredService<IOptions<AbpDistributedEventBusOptions>>().Value.Handlers)
var topicMetadatas = endpointContext.Endpoints.DataSources.SelectMany(x => x.Endpoints).OfType<RouteEndpoint>()
.Where(e => e.Metadata.GetOrderedMetadata<ITopicMetadata>().Any(t => t.Name != null))
.SelectMany(e => e.Metadata.GetOrderedMetadata<ITopicMetadata>())
.ToList();
var endpointConventionBuilder = endpointContext.Endpoints.MapPost(
"/api/abp/dapr/event", async httpContext =>
{
foreach (var @interface in handler.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDistributedEventHandler<>)))
{
var eventType = @interface.GetGenericArguments()[0];
var eventName = EventNameAttribute.GetNameOrDefault(eventType);
if (subscriptions.Any(x => x.PubsubName == daprEventBusOptions.PubSubName && x.Topic == eventName))
{
// Controllers with a [Topic] attribute can replace built-in event handlers.
continue;
}
var subscription = new AbpSubscription
{
PubsubName = daprEventBusOptions.PubSubName,
Topic = eventName,
Route = AbpAspNetCoreMvcDaprPubSubConsts.DaprEventCallbackUrl,
Metadata = new AbpMetadata
{
{
AbpMetadata.RawPayload, "true"
}
}
};
subscriptions.Add(subscription);
}
}
return Task.CompletedTask;
};
endpointContext.Endpoints.MapAbpSubscribeHandler(subscribeOptions);
await HandleEventAsync(httpContext);
});
var abpEvents = GetAbpEvents(endpointContext);
foreach (var @event in abpEvents.Where(x => !topicMetadatas.Any(t => t.PubsubName == x.PubsubName && t.Name == x.Name)))
{
endpointConventionBuilder.WithMetadata(new TopicAttribute(
@event.PubsubName,
@event.Name,
true));
}
endpointContext.Endpoints.MapSubscribeHandler();
});
});
}
private List<TopicAttribute> GetAbpEvents(EndpointRouteBuilderContext endpointContext)
{
var subscriptions = new List<TopicAttribute>();
var daprEventBusOptions = endpointContext.Endpoints.ServiceProvider.GetRequiredService<IOptions<AbpDaprEventBusOptions>>().Value;
foreach (var @interface in endpointContext.Endpoints.ServiceProvider.GetRequiredService<IOptions<AbpDistributedEventBusOptions>>().Value.Handlers
.SelectMany(x => x.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDistributedEventHandler<>))))
{
var eventType = @interface.GetGenericArguments()[0];
var eventName = EventNameAttribute.GetNameOrDefault(eventType);
var subscription = new TopicAttribute(daprEventBusOptions.PubSubName, eventName);
subscriptions.Add(subscription);
}
return subscriptions;
}
private async static Task HandleEventAsync(HttpContext httpContext)
{
var logger = httpContext.RequestServices.GetRequiredService<ILogger<AbpAspNetCoreMvcDaprEventBusModule>>();
httpContext.ValidateDaprAppApiToken();
var daprSerializer = httpContext.RequestServices.GetRequiredService<IDaprSerializer>();
var body = (await JsonDocument.ParseAsync(httpContext.Request.Body));
var pubSubName = body.RootElement.GetProperty("pubsubname").GetString();
var topic = body.RootElement.GetProperty("topic").GetString();
var data = body.RootElement.GetProperty("data").GetRawText();
if (pubSubName.IsNullOrWhiteSpace() || topic.IsNullOrWhiteSpace() || data.IsNullOrWhiteSpace())
{
logger.LogError("Invalid Dapr event request.");
httpContext.Response.StatusCode = 400;
return;
}
var distributedEventBus = httpContext.RequestServices.GetRequiredService<DaprDistributedEventBus>();
if (IsAbpDaprEventData(data))
{
var daprEventData = daprSerializer.Deserialize(data, typeof(AbpDaprEventData)).As<AbpDaprEventData>();
var eventData = daprSerializer.Deserialize(daprEventData.JsonData, distributedEventBus.GetEventType(daprEventData.Topic));
await distributedEventBus.TriggerHandlersAsync(distributedEventBus.GetEventType(daprEventData.Topic), eventData, daprEventData.MessageId, daprEventData.CorrelationId);
}
else
{
var eventData = daprSerializer.Deserialize(data, distributedEventBus.GetEventType(topic!));
await distributedEventBus.TriggerHandlersAsync(distributedEventBus.GetEventType(topic!), eventData);
}
httpContext.Response.StatusCode = 200;
}
private static bool IsAbpDaprEventData(string data)
{
var document = JsonDocument.Parse(data);
var objects = document.RootElement.EnumerateObject().ToList();
return objects.Count == 5 &&
objects.Any(x => x.Name.Equals("PubSubName", StringComparison.CurrentCultureIgnoreCase)) &&
objects.Any(x => x.Name.Equals("Topic", StringComparison.CurrentCultureIgnoreCase)) &&
objects.Any(x => x.Name.Equals("MessageId", StringComparison.CurrentCultureIgnoreCase)) &&
objects.Any(x => x.Name.Equals("JsonData", StringComparison.CurrentCultureIgnoreCase)) &&
objects.Any(x => x.Name.Equals("CorrelationId", StringComparison.CurrentCultureIgnoreCase));
}
}

6
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprPubSubConsts.cs

@ -1,6 +0,0 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public class AbpAspNetCoreMvcDaprPubSubConsts
{
public const string DaprEventCallbackUrl = "api/abp/dapr/event";
}

62
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Controllers/AbpAspNetCoreMvcDaprEventsController.cs

@ -1,62 +0,0 @@
using System;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp.Dapr;
using Volo.Abp.EventBus.Dapr;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Controllers;
[Area("abp")]
[RemoteService(Name = "abp")]
public class AbpAspNetCoreMvcDaprEventsController : AbpController
{
[HttpPost(AbpAspNetCoreMvcDaprPubSubConsts.DaprEventCallbackUrl)]
public virtual async Task<IActionResult> EventAsync()
{
HttpContext.ValidateDaprAppApiToken();
var daprSerializer = HttpContext.RequestServices.GetRequiredService<IDaprSerializer>();
var body = (await JsonDocument.ParseAsync(HttpContext.Request.Body));
var pubSubName = body.RootElement.GetProperty("pubsubname").GetString();
var topic = body.RootElement.GetProperty("topic").GetString();
var data = body.RootElement.GetProperty("data").GetRawText();
if (pubSubName.IsNullOrWhiteSpace() || topic.IsNullOrWhiteSpace() || data.IsNullOrWhiteSpace())
{
Logger.LogError("Invalid Dapr event request.");
return BadRequest();
}
var distributedEventBus = HttpContext.RequestServices.GetRequiredService<DaprDistributedEventBus>();
if (IsAbpDaprEventData(data))
{
var daprEventData = daprSerializer.Deserialize(data, typeof(AbpDaprEventData)).As<AbpDaprEventData>();
var eventData = daprSerializer.Deserialize(daprEventData.JsonData, distributedEventBus.GetEventType(daprEventData.Topic));
await distributedEventBus.TriggerHandlersAsync(distributedEventBus.GetEventType(daprEventData.Topic), eventData, daprEventData.MessageId, daprEventData.CorrelationId);
}
else
{
var eventData = daprSerializer.Deserialize(data, distributedEventBus.GetEventType(topic!));
await distributedEventBus.TriggerHandlersAsync(distributedEventBus.GetEventType(topic!), eventData);
}
return Ok();
}
protected virtual bool IsAbpDaprEventData(string data)
{
var document = JsonDocument.Parse(data);
var objects = document.RootElement.EnumerateObject().ToList();
return objects.Count == 5 &&
objects.Any(x => x.Name.Equals("PubSubName", StringComparison.CurrentCultureIgnoreCase)) &&
objects.Any(x => x.Name.Equals("Topic", StringComparison.CurrentCultureIgnoreCase)) &&
objects.Any(x => x.Name.Equals("MessageId", StringComparison.CurrentCultureIgnoreCase)) &&
objects.Any(x => x.Name.Equals("JsonData", StringComparison.CurrentCultureIgnoreCase)) &&
objects.Any(x => x.Name.Equals("CorrelationId", StringComparison.CurrentCultureIgnoreCase));
}
}

4
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs

@ -191,7 +191,9 @@ public class AuditingInterceptor : AbpInterceptor, ITransientDependency
}
if (!options.IsEnabledForGetRequests &&
invocation.Method.Name.StartsWith("Get", StringComparison.OrdinalIgnoreCase))
(string.Equals(auditLogInfo.HttpMethod, "Get", StringComparison.OrdinalIgnoreCase) ||
string.Equals(auditLogInfo.HttpMethod, "Head", StringComparison.OrdinalIgnoreCase) ||
invocation.Method.Name.StartsWith("Get", StringComparison.OrdinalIgnoreCase)))
{
return false;
}

3
framework/src/Volo.Abp.BlazoriseUI/Components/EntityAction.razor

@ -17,7 +17,8 @@
Disabled=@Disabled>
@if(!string.IsNullOrEmpty(Icon))
{
<Icon Name="@Icon" Class="me-1"/>
var iconClass = Text.IsNullOrEmpty() ? "" : "me-1";
<Icon Name="@Icon" Class="@iconClass"/>
}
<Span>
@Text

8
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/DistributedEventBusBase.cs

@ -132,7 +132,13 @@ public abstract class DistributedEventBusBase : EventBusBase, IDistributedEventB
Serialize(eventData),
Clock.Now
);
outgoingEventInfo.SetCorrelationId(CorrelationIdProvider.Get()!);
var correlationId = CorrelationIdProvider.Get();
if (correlationId != null)
{
outgoingEventInfo.SetCorrelationId(correlationId);
}
await eventOutbox.EnqueueAsync(outgoingEventInfo);
return true;
}

11
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientOptions.cs

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using Volo.Abp.Http.Client.ClientProxying;
using Volo.Abp.Http.Client.Proxying;
namespace Volo.Abp.Http.Client;
@ -8,8 +10,17 @@ public class AbpHttpClientOptions
{
public Dictionary<Type, HttpClientProxyConfig> HttpClientProxies { get; set; }
public Dictionary<string, List<Action<HttpClientProxyConfig, ClientProxyRequestContext, HttpClient>>> ProxyHttpClientPreSendActions { get; }
public AbpHttpClientOptions()
{
HttpClientProxies = new Dictionary<Type, HttpClientProxyConfig>();
ProxyHttpClientPreSendActions = new Dictionary<string, List<Action<HttpClientProxyConfig, ClientProxyRequestContext, HttpClient>>>();
}
public AbpHttpClientOptions AddPreSendAction(string remoteServiceName, Action<HttpClientProxyConfig, ClientProxyRequestContext, HttpClient> action)
{
ProxyHttpClientPreSendActions.GetOrAdd(remoteServiceName, () => new List<Action<HttpClientProxyConfig, ClientProxyRequestContext, HttpClient>>()).Add(action);
return this;
}
}

11
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs

@ -146,6 +146,11 @@ public class ClientProxyBase<TService> : ITransientDependency
HttpResponseMessage response;
try
{
foreach (var preSendAction in ClientOptions.Value.ProxyHttpClientPreSendActions.Where(x => x.Key == clientConfig.RemoteServiceName).SelectMany(x => x.Value))
{
preSendAction(clientConfig, requestContext, client);
}
response = await client.SendAsync(
requestMessage,
HttpCompletionOption.ResponseHeadersRead /*this will buffer only the headers, the content will be used as a stream*/,
@ -309,7 +314,11 @@ public class ClientProxyBase<TService> : ITransientDependency
}
//CorrelationId
requestMessage.Headers.Add(AbpCorrelationIdOptions.Value.HttpHeaderName, CorrelationIdProvider.Get());
var correlationId = CorrelationIdProvider.Get();
if (correlationId != null)
{
requestMessage.Headers.Add(AbpCorrelationIdOptions.Value.HttpHeaderName, correlationId);
}
//TenantId
if (CurrentTenant.Id.HasValue)

6
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs

@ -126,7 +126,11 @@ public class ApiDescriptionFinder : IApiDescriptionFinder, ITransientDependency
protected virtual void AddHeaders(HttpRequestMessage requestMessage)
{
//CorrelationId
requestMessage.Headers.Add(AbpCorrelationIdOptions.HttpHeaderName, CorrelationIdProvider.Get());
var correlationId = CorrelationIdProvider.Get();
if (correlationId != null)
{
requestMessage.Headers.Add(AbpCorrelationIdOptions.HttpHeaderName, correlationId);
}
//TenantId
if (CurrentTenant.Id.HasValue)

1
framework/src/Volo.Abp.Swashbuckle/Microsoft/AspNetCore/Builder/AbpSwaggerUIBuilderExtensions.cs

@ -16,7 +16,6 @@ public static class AbpSwaggerUIBuilderExtensions
return app.UseSwaggerUI(options =>
{
options.InjectJavascript("ui/abp.js");
options.InjectJavascript("ui/abp.swagger.js");
options.IndexStream = () => resolver?.Resolver();
setupAction?.Invoke(options);

4
framework/src/Volo.Abp.Swashbuckle/Volo/Abp/Swashbuckle/SwaggerHtmlResolver.cs

@ -10,12 +10,14 @@ public class SwaggerHtmlResolver : ISwaggerHtmlResolver, ITransientDependency
{
public virtual Stream Resolver()
{
var scriptBundleScript = "<script src=\"%(ScriptBundlePath)\" charset=\"utf-8\"></script>";
var abpSwaggerScript = "<script src=\"ui/abp.swagger.js\" charset=\"utf-8\"></script>";
var stream = typeof(SwaggerUIOptions).GetTypeInfo().Assembly
.GetManifestResourceStream("Swashbuckle.AspNetCore.SwaggerUI.index.html");
var html = new StreamReader(stream!)
.ReadToEnd()
.Replace("src=\"index.js\"", "src=\"ui/index.js\"");
.Replace(scriptBundleScript, $"{scriptBundleScript}\n{abpSwaggerScript}");
return new MemoryStream(Encoding.UTF8.GetBytes(html));
}

12
framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/abp.swagger.js

@ -1,9 +1,10 @@
var abp = abp || {};
(function () {
abp.SwaggerUIBundle = function (configObject) {
var oldSwaggerUIBundle = SwaggerUIBundle;
SwaggerUIBundle = function (configObject) {
var excludeUrl = ["swagger.json", "connect/token"]
var firstRequest = true;
var oidcSupportedFlows = configObject.oidcSupportedFlows || [];
@ -109,6 +110,9 @@ var abp = abp || {};
});
}
return SwaggerUIBundle(configObject);
return oldSwaggerUIBundle(configObject);
}
SwaggerUIBundle = Object.assign(SwaggerUIBundle, oldSwaggerUIBundle);
})();

75
framework/src/Volo.Abp.Swashbuckle/wwwroot/swagger/ui/index.js

@ -1,75 +0,0 @@
//Copy from https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/src/Swashbuckle.AspNetCore.SwaggerUI/index.js
/* Source: https://gist.github.com/lamberta/3768814
* Parse a string function definition and return a function object. Does not use eval.
* @param {string} str
* @return {function}
*
* Example:
* var f = function (x, y) { return x * y; };
* var g = parseFunction(f.toString());
* g(33, 3); //=> 99
*/
function parseFunction(str) {
if (!str) return void (0);
var fn_body_idx = str.indexOf('{'),
fn_body = str.substring(fn_body_idx + 1, str.lastIndexOf('}')),
fn_declare = str.substring(0, fn_body_idx),
fn_params = fn_declare.substring(fn_declare.indexOf('(') + 1, fn_declare.lastIndexOf(')')),
args = fn_params.split(',');
args.push(fn_body);
function Fn() {
return Function.apply(this, args);
}
Fn.prototype = Function.prototype;
return new Fn();
}
window.onload = function () {
var configObject = JSON.parse('%(ConfigObject)');
var oauthConfigObject = JSON.parse('%(OAuthConfigObject)');
// Workaround for https://github.com/swagger-api/swagger-ui/issues/5945
configObject.urls.forEach(function (item) {
if (item.url.startsWith("http") || item.url.startsWith("/")) return;
item.url = window.location.href.replace("index.html", item.url).split('#')[0];
});
// If validatorUrl is not explicitly provided, disable the feature by setting to null
if (!configObject.hasOwnProperty("validatorUrl"))
configObject.validatorUrl = null
// If oauth2RedirectUrl isn't specified, use the built-in default
if (!configObject.hasOwnProperty("oauth2RedirectUrl"))
configObject.oauth2RedirectUrl = (new URL("oauth2-redirect.html", window.location.href)).href;
// Apply mandatory parameters
configObject.dom_id = "#swagger-ui";
configObject.presets = [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset];
configObject.layout = "StandaloneLayout";
// Parse and add interceptor functions
var interceptors = JSON.parse('%(Interceptors)');
if (interceptors.RequestInterceptorFunction)
configObject.requestInterceptor = parseFunction(interceptors.RequestInterceptorFunction);
if (interceptors.ResponseInterceptorFunction)
configObject.responseInterceptor = parseFunction(interceptors.ResponseInterceptorFunction);
if (configObject.plugins) {
configObject.plugins = configObject.plugins.map(eval);
}
// Begin Swagger UI call region
const ui = abp.SwaggerUIBundle(configObject);
ui.initOAuth(oauthConfigObject);
// End Swagger UI call region
window.ui = ui
}

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

@ -1,8 +1,11 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.ClientProxying;
using Volo.Abp.Http.Client.Proxying;
using Volo.Abp.Http.DynamicProxying;
using Volo.Abp.Http.Localization;
using Volo.Abp.Localization;
@ -60,5 +63,16 @@ public class AbpHttpClientTestModule : AbpModule
options.FormDataConverts.Add(typeof(List<GetParamsNameValue>), typeof(TestObjectToFormData));
options.PathConverts.Add(typeof(int), typeof(TestObjectToPath));
});
Configure<AbpHttpClientOptions>(options =>
{
options.AddPreSendAction("Default", (_, requestContext, httpclient) =>
{
if (requestContext.Action.Name.Equals("TimeOutRequestAsync"))
{
httpclient.Timeout = TimeSpan.FromMilliseconds(1);
}
});
});
}
}

3
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs

@ -43,4 +43,7 @@ public interface IRegularTestController
Task<int> DeleteByIdAsync(int id);
Task<string> AbortRequestAsync(CancellationToken cancellationToken = default);
Task<string> TimeOutRequestAsync();
}

8
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs

@ -152,6 +152,14 @@ public class RegularTestController : AbpController, IRegularTestController
await Task.Delay(100, cancellationToken);
return "AbortRequestAsync";
}
[HttpGet]
[Route("timeout-request")]
public async Task<string> TimeOutRequestAsync()
{
await Task.Delay(100);
return "TimeOutRequestAsync";
}
}
public class Car

8
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_Tests.cs

@ -1,4 +1,5 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
@ -187,4 +188,11 @@ public class RegularTestControllerClientProxy_Tests : AbpHttpClientTestBase
var exception = await Assert.ThrowsAsync<AbpRemoteCallException>(async () => await _controller.AbortRequestAsync(cts.Token));
exception.InnerException.InnerException.InnerException.Message.ShouldBe("The client aborted the request.");
}
[Fact]
public async Task TimeOutRequestAsync()
{
var exception = await Assert.ThrowsAsync<HttpRequestException>(async () => await _controller.TimeOutRequestAsync());
exception.InnerException.InnerException.Message.ShouldBe("The client aborted the request.");
}
}

2
latest-versions.json

@ -1,6 +1,6 @@
[
{
"version": "9.0.0",
"version": "9.0.1",
"releaseDate": "",
"type": "stable",
"message": ""

6
modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json

@ -3,8 +3,8 @@
"name": "asp.net",
"private": true,
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.shared": "~9.0.0",
"@abp/prismjs": "~9.0.0",
"@abp/highlight.js": "~9.0.0"
"@abp/aspnetcore.mvc.ui.theme.shared": "~9.0.1",
"@abp/prismjs": "~9.0.1",
"@abp/highlight.js": "~9.0.1"
}
}

276
modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock

@ -2,212 +2,212 @@
# yarn lockfile v1
"@abp/aspnetcore.mvc.ui.theme.shared@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.0.0.tgz#a77b4b70fa6217146afd6a57211f57e2360564f2"
integrity sha512-QmZrSp+rfqfnC9W4QZ+nNdAq0rOALzRn1rsDpGDHhIHwyXB03QPdjzIWaEvldUYHzqg84cEP0LcqW4y57Xr1iw==
dependencies:
"@abp/aspnetcore.mvc.ui" "~9.0.0"
"@abp/bootstrap" "~9.0.0"
"@abp/bootstrap-datepicker" "~9.0.0"
"@abp/bootstrap-daterangepicker" "~9.0.0"
"@abp/datatables.net-bs5" "~9.0.0"
"@abp/font-awesome" "~9.0.0"
"@abp/jquery-form" "~9.0.0"
"@abp/jquery-validation-unobtrusive" "~9.0.0"
"@abp/lodash" "~9.0.0"
"@abp/luxon" "~9.0.0"
"@abp/malihu-custom-scrollbar-plugin" "~9.0.0"
"@abp/moment" "~9.0.0"
"@abp/select2" "~9.0.0"
"@abp/sweetalert2" "~9.0.0"
"@abp/timeago" "~9.0.0"
"@abp/toastr" "~9.0.0"
"@abp/aspnetcore.mvc.ui@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.0.0.tgz#93004dd056c130386c7b40c458f3a793d8c9e051"
integrity sha512-pvO5bh9baFcSbfW0s7tpMpbe6DKSC16jeyeluZLlvYxC0n2gkrv7H/hB5aMuBKIlh89aGY9DQIcMFC8iczL3Bg==
"@abp/aspnetcore.mvc.ui.theme.shared@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.0.1.tgz#7a1be780a305aa0d0f467472fc721b6f710fda7a"
integrity sha512-vrgRYKNctdZYhPCQ2I6Z10gUI1S5ynDj+DgnV7x7a7G/juuHaOSbkRXgwrmJsmYLfGA7KFCoJcbpER8mmXQeHQ==
dependencies:
"@abp/aspnetcore.mvc.ui" "~9.0.1"
"@abp/bootstrap" "~9.0.1"
"@abp/bootstrap-datepicker" "~9.0.1"
"@abp/bootstrap-daterangepicker" "~9.0.1"
"@abp/datatables.net-bs5" "~9.0.1"
"@abp/font-awesome" "~9.0.1"
"@abp/jquery-form" "~9.0.1"
"@abp/jquery-validation-unobtrusive" "~9.0.1"
"@abp/lodash" "~9.0.1"
"@abp/luxon" "~9.0.1"
"@abp/malihu-custom-scrollbar-plugin" "~9.0.1"
"@abp/moment" "~9.0.1"
"@abp/select2" "~9.0.1"
"@abp/sweetalert2" "~9.0.1"
"@abp/timeago" "~9.0.1"
"@abp/toastr" "~9.0.1"
"@abp/aspnetcore.mvc.ui@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.0.1.tgz#811dd91138efb82345fc06559b5d2feea88021af"
integrity sha512-KVbWCpBV7S1ViHYQP/hn4gSMzSiA2e8/zC0HND1fjE+1zXJqJyEWto1eg2DaKSOs/rmHD8WtFVMU4hCLQTCuIA==
dependencies:
ansi-colors "^4.1.3"
"@abp/bootstrap-datepicker@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.0.0.tgz#d6609e50568aac6a013abfcaa07817998e1eaa99"
integrity sha512-wxAeVsOVOJpidDn0g3FlJvpOsn5SVY5IpEG+FtUxSAKuWYJQWF6g5CaghrfS+FNDK9IHhxrgSMPMVBK9YlDSzw==
"@abp/bootstrap-datepicker@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.0.1.tgz#21cd19090b9a571ddf5a9019bc17864c8685408c"
integrity sha512-LhgqBRr66x9gGTAyv1FuPAw8u8N77WQK2AfDkNgLueurM2+xMYQzH3pWWLhcN676/JXafxfALCfBoM8U7CfXLg==
dependencies:
bootstrap-datepicker "^1.10.0"
"@abp/bootstrap-daterangepicker@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.0.0.tgz#fc2871e7d886accd8e4748cb763b695e10701c00"
integrity sha512-qS6SQR6X7wMCVLcuXatJ2+m1GNlpN71p7VDritxfwAMHa7WBRAAi8apX2WItwBAuLQ2s1uSA0Wp1KstHG3VPLA==
"@abp/bootstrap-daterangepicker@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.0.1.tgz#c19c23ede6fd868a147ea893d4fdecfa60b177f7"
integrity sha512-SlBVgiYa75uQyaGt5EVjduren7Po5UP26A1tXEQQr0hDia1+1TK0qxmJ2YxjeJpB7FwtcmJiofv5fngvJqhxIA==
dependencies:
bootstrap-daterangepicker "^3.1.0"
"@abp/bootstrap@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.0.0.tgz#5ab292e95ba7eef129cbaa1dc5f5d5c0af8530d7"
integrity sha512-jEr6su8BfoNxYvze/UlddE3DlezyPvQeiNhff8SDPqtRQ6XvjZ48O/JwtY01hK2njEDLOZINY5zJRGmO2OiOUw==
"@abp/bootstrap@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.0.1.tgz#2360303ec24c8f3eb755b10fa6a953db6d858daf"
integrity sha512-uAfVNsUYSsRqgeO9JMnhJJlFoDmO3/lTvER/iYOqerF0X03KQnR8HZy2V3MD1D+fSh/4JuU6aAZFl6IQLinMKQ==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
bootstrap "^5.3.3"
"@abp/clipboard@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.0.0.tgz#21ae924dfef6ccf3c8a3913bb81caf22b57404e4"
integrity sha512-W6S7pihpQ6Wfj48Clk7hYgDvUtnRwkNIsk5t8s9+LxoAdJQGQFxh7SAW1QNavUv8QqsClNE1VtSU87c7rSoAbA==
"@abp/clipboard@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.0.1.tgz#b1712b2d940caecc85eb99247a569ffe3ab1a387"
integrity sha512-obDYqUTf+65E26yHZO2DigQOeDnzAEvak0Umz91cholZHVRvzc9X1itv/abS/whU0YOU/d8Viud8zFv1MnM83w==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
clipboard "^2.0.11"
"@abp/core@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.0.0.tgz#dc5ea9139957e307f73ac1f40b179f991e3c2168"
integrity sha512-vzQyTpqVDx63pOI9e2QLUMWZCrSojHsRnzLlvl+/cKFi7jE1q/uSP66FPR2bwkD+DNLI/tn6is1Jx3skKA1FvA==
"@abp/core@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.0.1.tgz#727fc1dfac21cca1216c4f5091da7d5efefdb467"
integrity sha512-NWXDVCIJePbcKCrRvGxAlaA2MrOF2IZox37bQkW7/mTeOPpNoTa6lhowksNNcTpFq2YdD8bG0i0kfskkp3EZ2w==
dependencies:
"@abp/utils" "~9.0.0"
"@abp/utils" "~9.0.1"
"@abp/datatables.net-bs5@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.0.0.tgz#4daee90bdb6446e854ce65f2a3afabb2a13e49ae"
integrity sha512-ln10tcRwxcB0nuSgqJ/66strHsqMHDC03c0ktxEplAN34yP7KInjFf/U9Svo74K0nKlkJdMCgmqmT4CnBJZlIg==
"@abp/datatables.net-bs5@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.0.1.tgz#61e2a3daacf1c074ed6b459e25084cc1f5c4eeab"
integrity sha512-tAHdf4TBAS0bVRcvovDKS0OHFyeRSPYbiSIwDn8AIWzW9Weav31jhZ5jL3HEtBKVdb7zqUQ8/3dYPufjDuDdDA==
dependencies:
"@abp/datatables.net" "~9.0.0"
"@abp/datatables.net" "~9.0.1"
datatables.net-bs5 "^2.1.8"
"@abp/datatables.net@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.0.0.tgz#82b28518293c9091ec72d5defae2f5d2903d8bbc"
integrity sha512-QhuX3bGZOckM/fhIIGj/StMJIX2BBtNbLzjTR7WnKMMxDkv0+oDvjw6OqqpYKSvj/q3vY6sRWuCNdEzNi0Fk6A==
"@abp/datatables.net@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.0.1.tgz#4cf193e7493f85a272de47326a32a021e3ba40f5"
integrity sha512-JY53zVQPluRrBiKwtgk4gfSgD8VDs/VU8LWizhGCprXt3h9vXx0SQL4My6zvXVkv4hUjaq4+MYo84OI8EwFvsg==
dependencies:
"@abp/jquery" "~9.0.0"
"@abp/jquery" "~9.0.1"
datatables.net "^2.1.8"
"@abp/font-awesome@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.0.0.tgz#51cce18434a51d050ebb0d822a8c193b7e32f73d"
integrity sha512-bEHRVzOk0V2kFMAvB/YXFYaDKWRFO0+ScO7jrEFtFgdHXBcqy+dk1aM/N69WalyNmjRRpnh03GhH1LRpwL4/xA==
"@abp/font-awesome@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.0.1.tgz#69da15018e7e5f2ff0d3c0d113a4325f54ffc0c3"
integrity sha512-7NnYBmk0plLVElUWR1zohul87vE9wKJItemWT5Kskf/IJirKOvXwSSu+7Jg1LmnVtIxcH6qgRpJMmzuAJgawnQ==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
"@fortawesome/fontawesome-free" "^6.6.0"
"@abp/highlight.js@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-9.0.0.tgz#6d924c9bb3d6b11dcd66a9a71aea873c9866ed6c"
integrity sha512-eGvgMUNYKW2B2h3QcpMRniaoQ1/9RJBXkn2VsyM8lVSfrfbOg26mMyw7J2BXvuEnXqtnVdFEG45cSFlAnTAVXQ==
"@abp/highlight.js@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-9.0.1.tgz#22f135e73d5ee5ce14e31fe6c1a1b3eee6a91f55"
integrity sha512-K1EXCtRsc116c7UYcUXRVmrPFHDY4531+ij9P32EdP1/3AIwm50r+tz2epZV2GyIuGw/mVWyFB0vduqFlr0W3g==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
"@highlightjs/cdn-assets" "~11.10.0"
"@abp/jquery-form@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.0.0.tgz#6518eb117ee35c6bc57543fd1fccb0a8cf8e22f4"
integrity sha512-6qpfPGHrjADmm7Ne3zcXBGPuLngG8ldAn5fk9k2taftpyX0iR67/nZxiQ0ujaQGgaai5Wb3imNXMG96XLnubkQ==
"@abp/jquery-form@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.0.1.tgz#35e0a23cf50b00e0d3b0886f097233b79144145b"
integrity sha512-9co0+Cver+RUSOpfuukSWQhR+TCnO9Dly4RTVRIKFksGVfN8hA70NqRrVxaASNNFv42ynK88FQK+2bLPswz/iw==
dependencies:
"@abp/jquery" "~9.0.0"
"@abp/jquery" "~9.0.1"
jquery-form "^4.3.0"
"@abp/jquery-validation-unobtrusive@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.0.0.tgz#8ee87dbdec5e92ab04ae9642a49af1598af31c1a"
integrity sha512-KlRQ1htyFmAIMS9xbHvHIcdhAgtPFe/u7c87MiWlk/CdhDrI3J+n5biLvQUzxCC+YWlwx27zBtPKx2vLeMRH6Q==
"@abp/jquery-validation-unobtrusive@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.0.1.tgz#f9564aabebf4d9f90b4ee5c3f3de0e1a348d8ecc"
integrity sha512-Zpz48wIFjq7eIApG/sEVjkfaJWZhcyCnlCtDf5eU3vvh82nRetbiCEAVLpZ4LVjorlyp3VvVAxReUSeeWflu8A==
dependencies:
"@abp/jquery-validation" "~9.0.0"
"@abp/jquery-validation" "~9.0.1"
jquery-validation-unobtrusive "^4.0.0"
"@abp/jquery-validation@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.0.0.tgz#681d8037c57b3875bd51c927e350ffce65d7c5d2"
integrity sha512-XgCUBFWbhTDcGepD/xkwA/1Hl4TyYitRttauDJk3tuyXhNtykvdugCCOPY4j2s6VoT1Sb/TFFvOcgEvlBwkKiw==
"@abp/jquery-validation@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.0.1.tgz#ad37a44e82fb32ec9bfe2b736dfc8a143fca89c7"
integrity sha512-uFSxiPMEkaPlcuyXIoKbeOIIRHo4IrgRbmnlHwI1uETrW60ENUdE2LJAVcYS77JROrjVtv8FPlaffWuva6sGeQ==
dependencies:
"@abp/jquery" "~9.0.0"
"@abp/jquery" "~9.0.1"
jquery-validation "^1.21.0"
"@abp/jquery@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.0.0.tgz#6c67a9f927be598ab6201f8888e8a3cb8bba0686"
integrity sha512-cUXA7v6F1HqAc/w8fQGxHIfA/6BqO+4uHXDnSsJZtrI26DTLOgw5kBNnAz2W4xggGPCY7SKFHNRZuOW/QfVRPg==
"@abp/jquery@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.0.1.tgz#ecce14644c753c581caee0df7bfafd3d083e70f0"
integrity sha512-z8EcnOveItAszbTsM9GFFDQSlcIg/ym6+WZME9uldLoetdxkmI9ij0knVeGj5eqMjQaHvTWwrVLObiBbZwYFWw==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
jquery "~3.7.1"
"@abp/lodash@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.0.0.tgz#a4d42b2dfeb37859ff9f0079c7a8a6cabc982efe"
integrity sha512-xoAsOkkgnQdPH2OyFTgF6OlCLVbyGF4u8CrFcZWCZmuIFT9cg0+ZEiq//Vog1pg3Lf//N37ceGpOag1LwMe6cQ==
"@abp/lodash@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.0.1.tgz#c52c8a4125ecfbeaad7badc5c28bf86d5168799a"
integrity sha512-D+/iOyh/O+jV80bK5vWxyn++IVmeRbZq/UaXanA4RKpzigxJUsgOiSD9lUj85BkfKn2YyFmoR39xwaAqkWoyRA==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
lodash "^4.17.21"
"@abp/luxon@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.0.0.tgz#a54dda13cbbd362481994d20d2e07c669c6ce109"
integrity sha512-aPejQnoVPfm60LnjwhfUkIsB+poNWusrBE0IHllxhvOVE+01nfa1Lvpp5AUWzV+XchPGOCSf+a29ds4EQWfNbg==
"@abp/luxon@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.0.1.tgz#ac59a56653d51b997dbbde8ccc58dac5746a9ef4"
integrity sha512-1ZHXP5SGz05tQ1tzYmPcRiZwmkCKGX0KWFoV0zF3ATRaevzku9nXvSy7K6GIFkkgTb9f9XNx4/OBBF/GXkuRqw==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
luxon "^3.5.0"
"@abp/malihu-custom-scrollbar-plugin@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.0.0.tgz#b8a49202d531c711874a2bcdae5081ae0ed34c3f"
integrity sha512-iSZ9lKMnv5F2SplR+A4blhFEDq79lKbvBQuS7h/Di3KEHFOdJY4pA8NP8OR+PXOF3JfAezGyjqy7ydn/JNPRwA==
"@abp/malihu-custom-scrollbar-plugin@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.0.1.tgz#9e60bcfb9f9d76fed9c50e45bf490d5abd0f4689"
integrity sha512-GkKPLg9Z9UTTSSpW2hYZYZ9IgCvDSOYJmyRSl/h4fG8cWMgGCPVpv8MPPu0+WtIfwW/mT4SPX9X+sjUlRL5XRQ==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
malihu-custom-scrollbar-plugin "^3.1.5"
"@abp/moment@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.0.0.tgz#9583e97472ddf608a9a5a6b8a37fee602b419900"
integrity sha512-wgyJSKmBmHLXZJN3dy3zK4i2JvWGgqnvdtrKURy/raEurxlymv8JF5JA971kTv6Kz2OA/OSYttpVJkDGUpPAhA==
"@abp/moment@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.0.1.tgz#d67fa25017fe15dae10e0a0fcebb03aeb63d31d3"
integrity sha512-bTuCc7pi/DCtgz7t96UBU0wn9UGt4b50JhDCVeTKquh+gP1sgWoI5hyvEBkmgE4dcffO0yzZU2opHYTKYGl/OQ==
dependencies:
moment "^2.30.1"
"@abp/prismjs@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.0.0.tgz#2ac4a5ddc0c8e0462af027a4dd97a177a3d6f7a5"
integrity sha512-+evmBq5qV+Ix/TNqO2ljJn0jygW2rEgKNtABcBGmrZC8xiI7+2hjKmSE0e43qb+RDib8aJjwH8ya8wF7p68ZFw==
"@abp/prismjs@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.0.1.tgz#2693014c8161d561e7c7bef01d5cd24ee53be1b3"
integrity sha512-NwW+vpTiAsuJYNC/MEiAj0dGMWml95mAQD1Z0Djfsg9D6OpcLsdlgWX+KVdZvHoJ+SPgTDIqdt/0LnIv1w/XdA==
dependencies:
"@abp/clipboard" "~9.0.0"
"@abp/core" "~9.0.0"
"@abp/clipboard" "~9.0.1"
"@abp/core" "~9.0.1"
prismjs "^1.29.0"
"@abp/select2@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.0.0.tgz#71503eebb746f40a0e565dba1efa05d6ae937b77"
integrity sha512-l+Hz2Qmc7AcyaTJOyuDaV99RbBWYxYxbeyVEc4xKbRFbJxYBUxT/DFlyozEaoz+OU1fl4szO3UCthJ2yZvxCdg==
"@abp/select2@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.0.1.tgz#9f1c5c27e72bef910fdf63fc3167f415d2ba8db8"
integrity sha512-epyYJH2gAOU2UvGn5FD6372hbW3fL7uZIgGpvfgEfa+l6HPdlV9B1OweU2xkONNCoylShAcP/y8qf+bUh8WDEA==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
select2 "^4.0.13"
"@abp/sweetalert2@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.0.0.tgz#71e511117c18b515e5c658e9cc0667770af65d91"
integrity sha512-wxDqoA6RUhkbcN27Kzslu/m2ai5T9AkJRtgs7pV55LvPv1ohuCPEUeTVVh8S/5Pgmr7aPFAPxaaCO32aPcImPw==
"@abp/sweetalert2@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.0.1.tgz#a364b21665cb76e92640496dc8a77b8edbce4500"
integrity sha512-0RXR/lzy3qMJLisGUz4qrcA9Clu/Tt1ABGzB64ynG1t/l5XessQsIB5Hkt6MbSyOEsprRsq+ZtKH35CwTRPdAw==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
sweetalert2 "^11.14.1"
"@abp/timeago@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.0.0.tgz#8f262ba2a74e1c133d0c129f8a0bc55c29441e1a"
integrity sha512-eaNdIUEtDXnuYqS5O7EGRV+j6XOLAsxz7gd5BIKR9lDujd8pEjSJkXKO+/0KxLTkUSy3JudIO37K1h2wTpbYZA==
"@abp/timeago@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.0.1.tgz#d1b7e11fdc267bdb736ff4b591afd3fb81c07cce"
integrity sha512-dNfUQwilrY+JHxL5lwJ++ixYDGAs5lF1EDxKMQMIXD/ak5L8rkdLns8Otta7FhgDDny9UJ7XWQjYsG8ZxxybRQ==
dependencies:
"@abp/jquery" "~9.0.0"
"@abp/jquery" "~9.0.1"
timeago "^1.6.7"
"@abp/toastr@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-9.0.0.tgz#74d74ecb3e7f2d63b2ff7735c89a40deb3dc3a13"
integrity sha512-n9uJvw8l2HoxeowPO4diEMxwchGh6puefhyRljFAAstzK77Mhn0u2J7kPynNv94rLSYTYoCxUeJ4TTN+/TVDGQ==
"@abp/toastr@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-9.0.1.tgz#2105a639d7cd4fa9c671b4215d719a78fef7e0ee"
integrity sha512-10KuHnSJOxkvzqs6Aim6GJp8xBuT+TsZqBE1KaXQnERPawmOo4+ME28kA/7lq8fvgFUNO3K41T2uuA1GPHVQcg==
dependencies:
"@abp/jquery" "~9.0.0"
"@abp/jquery" "~9.0.1"
toastr "^2.1.4"
"@abp/utils@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.0.0.tgz#2264fd83105b061860edd98dbe0efdfe55803ed5"
integrity sha512-/SB4VYk3Av+ltXv6ovkFHG4iWiECLLitaYWcEHxVAO80ufNjrNnWpHFpXHP8u/RpXIp/CoNiHLr6j5TkZUTZjw==
"@abp/utils@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.0.1.tgz#dccaf7cf92147304d454ffb9a8664d52dc7e5c5a"
integrity sha512-7eOKT0ciKNdtuASck1kifyc4dC6CeebgxV+rx/EAi3YwPMDkqie6YwLGUqKdGW2N/MZ15JuDR6gkuZxKHEb0NQ==
dependencies:
just-compare "^2.3.0"

4
modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json

@ -3,8 +3,8 @@
"name": "asp.net",
"private": true,
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.basic": "~9.0.0",
"@abp/prismjs": "~9.0.0"
"@abp/aspnetcore.mvc.ui.theme.basic": "~9.0.1",
"@abp/prismjs": "~9.0.1"
},
"devDependencies": {}
}

280
modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock

@ -2,211 +2,211 @@
# yarn lockfile v1
"@abp/aspnetcore.mvc.ui.theme.basic@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.0.0.tgz#e4b05d63446c24b612275e3949881e176d32527f"
integrity sha512-GHEt26xESe/TCg1q6ZjeUbxniqdxaiB1OPTZ9md7ONql6bEp8t9iUPeSlkyAZtQ7qBzwgkiLpkRFT+J21DftaQ==
dependencies:
"@abp/aspnetcore.mvc.ui.theme.shared" "~9.0.0"
"@abp/aspnetcore.mvc.ui.theme.shared@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.0.0.tgz#a77b4b70fa6217146afd6a57211f57e2360564f2"
integrity sha512-QmZrSp+rfqfnC9W4QZ+nNdAq0rOALzRn1rsDpGDHhIHwyXB03QPdjzIWaEvldUYHzqg84cEP0LcqW4y57Xr1iw==
dependencies:
"@abp/aspnetcore.mvc.ui" "~9.0.0"
"@abp/bootstrap" "~9.0.0"
"@abp/bootstrap-datepicker" "~9.0.0"
"@abp/bootstrap-daterangepicker" "~9.0.0"
"@abp/datatables.net-bs5" "~9.0.0"
"@abp/font-awesome" "~9.0.0"
"@abp/jquery-form" "~9.0.0"
"@abp/jquery-validation-unobtrusive" "~9.0.0"
"@abp/lodash" "~9.0.0"
"@abp/luxon" "~9.0.0"
"@abp/malihu-custom-scrollbar-plugin" "~9.0.0"
"@abp/moment" "~9.0.0"
"@abp/select2" "~9.0.0"
"@abp/sweetalert2" "~9.0.0"
"@abp/timeago" "~9.0.0"
"@abp/toastr" "~9.0.0"
"@abp/aspnetcore.mvc.ui@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.0.0.tgz#93004dd056c130386c7b40c458f3a793d8c9e051"
integrity sha512-pvO5bh9baFcSbfW0s7tpMpbe6DKSC16jeyeluZLlvYxC0n2gkrv7H/hB5aMuBKIlh89aGY9DQIcMFC8iczL3Bg==
"@abp/aspnetcore.mvc.ui.theme.basic@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-9.0.1.tgz#cdb6130ac6f8499f9a3f856b596c46745d056a30"
integrity sha512-JaxuDH8WTnZYX6C7CX7ZVHfhTDR/SxfwRvtJaGGijSX8QKd1DXpfi9lYEF4Y9k5b48YT/0slBqU5g0sLmBd3+g==
dependencies:
"@abp/aspnetcore.mvc.ui.theme.shared" "~9.0.1"
"@abp/aspnetcore.mvc.ui.theme.shared@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-9.0.1.tgz#7a1be780a305aa0d0f467472fc721b6f710fda7a"
integrity sha512-vrgRYKNctdZYhPCQ2I6Z10gUI1S5ynDj+DgnV7x7a7G/juuHaOSbkRXgwrmJsmYLfGA7KFCoJcbpER8mmXQeHQ==
dependencies:
"@abp/aspnetcore.mvc.ui" "~9.0.1"
"@abp/bootstrap" "~9.0.1"
"@abp/bootstrap-datepicker" "~9.0.1"
"@abp/bootstrap-daterangepicker" "~9.0.1"
"@abp/datatables.net-bs5" "~9.0.1"
"@abp/font-awesome" "~9.0.1"
"@abp/jquery-form" "~9.0.1"
"@abp/jquery-validation-unobtrusive" "~9.0.1"
"@abp/lodash" "~9.0.1"
"@abp/luxon" "~9.0.1"
"@abp/malihu-custom-scrollbar-plugin" "~9.0.1"
"@abp/moment" "~9.0.1"
"@abp/select2" "~9.0.1"
"@abp/sweetalert2" "~9.0.1"
"@abp/timeago" "~9.0.1"
"@abp/toastr" "~9.0.1"
"@abp/aspnetcore.mvc.ui@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-9.0.1.tgz#811dd91138efb82345fc06559b5d2feea88021af"
integrity sha512-KVbWCpBV7S1ViHYQP/hn4gSMzSiA2e8/zC0HND1fjE+1zXJqJyEWto1eg2DaKSOs/rmHD8WtFVMU4hCLQTCuIA==
dependencies:
ansi-colors "^4.1.3"
"@abp/bootstrap-datepicker@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.0.0.tgz#d6609e50568aac6a013abfcaa07817998e1eaa99"
integrity sha512-wxAeVsOVOJpidDn0g3FlJvpOsn5SVY5IpEG+FtUxSAKuWYJQWF6g5CaghrfS+FNDK9IHhxrgSMPMVBK9YlDSzw==
"@abp/bootstrap-datepicker@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-9.0.1.tgz#21cd19090b9a571ddf5a9019bc17864c8685408c"
integrity sha512-LhgqBRr66x9gGTAyv1FuPAw8u8N77WQK2AfDkNgLueurM2+xMYQzH3pWWLhcN676/JXafxfALCfBoM8U7CfXLg==
dependencies:
bootstrap-datepicker "^1.10.0"
"@abp/bootstrap-daterangepicker@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.0.0.tgz#fc2871e7d886accd8e4748cb763b695e10701c00"
integrity sha512-qS6SQR6X7wMCVLcuXatJ2+m1GNlpN71p7VDritxfwAMHa7WBRAAi8apX2WItwBAuLQ2s1uSA0Wp1KstHG3VPLA==
"@abp/bootstrap-daterangepicker@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-9.0.1.tgz#c19c23ede6fd868a147ea893d4fdecfa60b177f7"
integrity sha512-SlBVgiYa75uQyaGt5EVjduren7Po5UP26A1tXEQQr0hDia1+1TK0qxmJ2YxjeJpB7FwtcmJiofv5fngvJqhxIA==
dependencies:
bootstrap-daterangepicker "^3.1.0"
"@abp/bootstrap@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.0.0.tgz#5ab292e95ba7eef129cbaa1dc5f5d5c0af8530d7"
integrity sha512-jEr6su8BfoNxYvze/UlddE3DlezyPvQeiNhff8SDPqtRQ6XvjZ48O/JwtY01hK2njEDLOZINY5zJRGmO2OiOUw==
"@abp/bootstrap@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-9.0.1.tgz#2360303ec24c8f3eb755b10fa6a953db6d858daf"
integrity sha512-uAfVNsUYSsRqgeO9JMnhJJlFoDmO3/lTvER/iYOqerF0X03KQnR8HZy2V3MD1D+fSh/4JuU6aAZFl6IQLinMKQ==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
bootstrap "^5.3.3"
"@abp/clipboard@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.0.0.tgz#21ae924dfef6ccf3c8a3913bb81caf22b57404e4"
integrity sha512-W6S7pihpQ6Wfj48Clk7hYgDvUtnRwkNIsk5t8s9+LxoAdJQGQFxh7SAW1QNavUv8QqsClNE1VtSU87c7rSoAbA==
"@abp/clipboard@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-9.0.1.tgz#b1712b2d940caecc85eb99247a569ffe3ab1a387"
integrity sha512-obDYqUTf+65E26yHZO2DigQOeDnzAEvak0Umz91cholZHVRvzc9X1itv/abS/whU0YOU/d8Viud8zFv1MnM83w==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
clipboard "^2.0.11"
"@abp/core@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.0.0.tgz#dc5ea9139957e307f73ac1f40b179f991e3c2168"
integrity sha512-vzQyTpqVDx63pOI9e2QLUMWZCrSojHsRnzLlvl+/cKFi7jE1q/uSP66FPR2bwkD+DNLI/tn6is1Jx3skKA1FvA==
"@abp/core@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/core/-/core-9.0.1.tgz#727fc1dfac21cca1216c4f5091da7d5efefdb467"
integrity sha512-NWXDVCIJePbcKCrRvGxAlaA2MrOF2IZox37bQkW7/mTeOPpNoTa6lhowksNNcTpFq2YdD8bG0i0kfskkp3EZ2w==
dependencies:
"@abp/utils" "~9.0.0"
"@abp/utils" "~9.0.1"
"@abp/datatables.net-bs5@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.0.0.tgz#4daee90bdb6446e854ce65f2a3afabb2a13e49ae"
integrity sha512-ln10tcRwxcB0nuSgqJ/66strHsqMHDC03c0ktxEplAN34yP7KInjFf/U9Svo74K0nKlkJdMCgmqmT4CnBJZlIg==
"@abp/datatables.net-bs5@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-9.0.1.tgz#61e2a3daacf1c074ed6b459e25084cc1f5c4eeab"
integrity sha512-tAHdf4TBAS0bVRcvovDKS0OHFyeRSPYbiSIwDn8AIWzW9Weav31jhZ5jL3HEtBKVdb7zqUQ8/3dYPufjDuDdDA==
dependencies:
"@abp/datatables.net" "~9.0.0"
"@abp/datatables.net" "~9.0.1"
datatables.net-bs5 "^2.1.8"
"@abp/datatables.net@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.0.0.tgz#82b28518293c9091ec72d5defae2f5d2903d8bbc"
integrity sha512-QhuX3bGZOckM/fhIIGj/StMJIX2BBtNbLzjTR7WnKMMxDkv0+oDvjw6OqqpYKSvj/q3vY6sRWuCNdEzNi0Fk6A==
"@abp/datatables.net@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-9.0.1.tgz#4cf193e7493f85a272de47326a32a021e3ba40f5"
integrity sha512-JY53zVQPluRrBiKwtgk4gfSgD8VDs/VU8LWizhGCprXt3h9vXx0SQL4My6zvXVkv4hUjaq4+MYo84OI8EwFvsg==
dependencies:
"@abp/jquery" "~9.0.0"
"@abp/jquery" "~9.0.1"
datatables.net "^2.1.8"
"@abp/font-awesome@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.0.0.tgz#51cce18434a51d050ebb0d822a8c193b7e32f73d"
integrity sha512-bEHRVzOk0V2kFMAvB/YXFYaDKWRFO0+ScO7jrEFtFgdHXBcqy+dk1aM/N69WalyNmjRRpnh03GhH1LRpwL4/xA==
"@abp/font-awesome@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-9.0.1.tgz#69da15018e7e5f2ff0d3c0d113a4325f54ffc0c3"
integrity sha512-7NnYBmk0plLVElUWR1zohul87vE9wKJItemWT5Kskf/IJirKOvXwSSu+7Jg1LmnVtIxcH6qgRpJMmzuAJgawnQ==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
"@fortawesome/fontawesome-free" "^6.6.0"
"@abp/jquery-form@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.0.0.tgz#6518eb117ee35c6bc57543fd1fccb0a8cf8e22f4"
integrity sha512-6qpfPGHrjADmm7Ne3zcXBGPuLngG8ldAn5fk9k2taftpyX0iR67/nZxiQ0ujaQGgaai5Wb3imNXMG96XLnubkQ==
"@abp/jquery-form@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-9.0.1.tgz#35e0a23cf50b00e0d3b0886f097233b79144145b"
integrity sha512-9co0+Cver+RUSOpfuukSWQhR+TCnO9Dly4RTVRIKFksGVfN8hA70NqRrVxaASNNFv42ynK88FQK+2bLPswz/iw==
dependencies:
"@abp/jquery" "~9.0.0"
"@abp/jquery" "~9.0.1"
jquery-form "^4.3.0"
"@abp/jquery-validation-unobtrusive@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.0.0.tgz#8ee87dbdec5e92ab04ae9642a49af1598af31c1a"
integrity sha512-KlRQ1htyFmAIMS9xbHvHIcdhAgtPFe/u7c87MiWlk/CdhDrI3J+n5biLvQUzxCC+YWlwx27zBtPKx2vLeMRH6Q==
"@abp/jquery-validation-unobtrusive@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-9.0.1.tgz#f9564aabebf4d9f90b4ee5c3f3de0e1a348d8ecc"
integrity sha512-Zpz48wIFjq7eIApG/sEVjkfaJWZhcyCnlCtDf5eU3vvh82nRetbiCEAVLpZ4LVjorlyp3VvVAxReUSeeWflu8A==
dependencies:
"@abp/jquery-validation" "~9.0.0"
"@abp/jquery-validation" "~9.0.1"
jquery-validation-unobtrusive "^4.0.0"
"@abp/jquery-validation@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.0.0.tgz#681d8037c57b3875bd51c927e350ffce65d7c5d2"
integrity sha512-XgCUBFWbhTDcGepD/xkwA/1Hl4TyYitRttauDJk3tuyXhNtykvdugCCOPY4j2s6VoT1Sb/TFFvOcgEvlBwkKiw==
"@abp/jquery-validation@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-9.0.1.tgz#ad37a44e82fb32ec9bfe2b736dfc8a143fca89c7"
integrity sha512-uFSxiPMEkaPlcuyXIoKbeOIIRHo4IrgRbmnlHwI1uETrW60ENUdE2LJAVcYS77JROrjVtv8FPlaffWuva6sGeQ==
dependencies:
"@abp/jquery" "~9.0.0"
"@abp/jquery" "~9.0.1"
jquery-validation "^1.21.0"
"@abp/jquery@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.0.0.tgz#6c67a9f927be598ab6201f8888e8a3cb8bba0686"
integrity sha512-cUXA7v6F1HqAc/w8fQGxHIfA/6BqO+4uHXDnSsJZtrI26DTLOgw5kBNnAz2W4xggGPCY7SKFHNRZuOW/QfVRPg==
"@abp/jquery@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-9.0.1.tgz#ecce14644c753c581caee0df7bfafd3d083e70f0"
integrity sha512-z8EcnOveItAszbTsM9GFFDQSlcIg/ym6+WZME9uldLoetdxkmI9ij0knVeGj5eqMjQaHvTWwrVLObiBbZwYFWw==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
jquery "~3.7.1"
"@abp/lodash@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.0.0.tgz#a4d42b2dfeb37859ff9f0079c7a8a6cabc982efe"
integrity sha512-xoAsOkkgnQdPH2OyFTgF6OlCLVbyGF4u8CrFcZWCZmuIFT9cg0+ZEiq//Vog1pg3Lf//N37ceGpOag1LwMe6cQ==
"@abp/lodash@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-9.0.1.tgz#c52c8a4125ecfbeaad7badc5c28bf86d5168799a"
integrity sha512-D+/iOyh/O+jV80bK5vWxyn++IVmeRbZq/UaXanA4RKpzigxJUsgOiSD9lUj85BkfKn2YyFmoR39xwaAqkWoyRA==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
lodash "^4.17.21"
"@abp/luxon@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.0.0.tgz#a54dda13cbbd362481994d20d2e07c669c6ce109"
integrity sha512-aPejQnoVPfm60LnjwhfUkIsB+poNWusrBE0IHllxhvOVE+01nfa1Lvpp5AUWzV+XchPGOCSf+a29ds4EQWfNbg==
"@abp/luxon@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-9.0.1.tgz#ac59a56653d51b997dbbde8ccc58dac5746a9ef4"
integrity sha512-1ZHXP5SGz05tQ1tzYmPcRiZwmkCKGX0KWFoV0zF3ATRaevzku9nXvSy7K6GIFkkgTb9f9XNx4/OBBF/GXkuRqw==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
luxon "^3.5.0"
"@abp/malihu-custom-scrollbar-plugin@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.0.0.tgz#b8a49202d531c711874a2bcdae5081ae0ed34c3f"
integrity sha512-iSZ9lKMnv5F2SplR+A4blhFEDq79lKbvBQuS7h/Di3KEHFOdJY4pA8NP8OR+PXOF3JfAezGyjqy7ydn/JNPRwA==
"@abp/malihu-custom-scrollbar-plugin@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-9.0.1.tgz#9e60bcfb9f9d76fed9c50e45bf490d5abd0f4689"
integrity sha512-GkKPLg9Z9UTTSSpW2hYZYZ9IgCvDSOYJmyRSl/h4fG8cWMgGCPVpv8MPPu0+WtIfwW/mT4SPX9X+sjUlRL5XRQ==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
malihu-custom-scrollbar-plugin "^3.1.5"
"@abp/moment@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.0.0.tgz#9583e97472ddf608a9a5a6b8a37fee602b419900"
integrity sha512-wgyJSKmBmHLXZJN3dy3zK4i2JvWGgqnvdtrKURy/raEurxlymv8JF5JA971kTv6Kz2OA/OSYttpVJkDGUpPAhA==
"@abp/moment@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-9.0.1.tgz#d67fa25017fe15dae10e0a0fcebb03aeb63d31d3"
integrity sha512-bTuCc7pi/DCtgz7t96UBU0wn9UGt4b50JhDCVeTKquh+gP1sgWoI5hyvEBkmgE4dcffO0yzZU2opHYTKYGl/OQ==
dependencies:
moment "^2.30.1"
"@abp/prismjs@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.0.0.tgz#2ac4a5ddc0c8e0462af027a4dd97a177a3d6f7a5"
integrity sha512-+evmBq5qV+Ix/TNqO2ljJn0jygW2rEgKNtABcBGmrZC8xiI7+2hjKmSE0e43qb+RDib8aJjwH8ya8wF7p68ZFw==
"@abp/prismjs@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-9.0.1.tgz#2693014c8161d561e7c7bef01d5cd24ee53be1b3"
integrity sha512-NwW+vpTiAsuJYNC/MEiAj0dGMWml95mAQD1Z0Djfsg9D6OpcLsdlgWX+KVdZvHoJ+SPgTDIqdt/0LnIv1w/XdA==
dependencies:
"@abp/clipboard" "~9.0.0"
"@abp/core" "~9.0.0"
"@abp/clipboard" "~9.0.1"
"@abp/core" "~9.0.1"
prismjs "^1.29.0"
"@abp/select2@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.0.0.tgz#71503eebb746f40a0e565dba1efa05d6ae937b77"
integrity sha512-l+Hz2Qmc7AcyaTJOyuDaV99RbBWYxYxbeyVEc4xKbRFbJxYBUxT/DFlyozEaoz+OU1fl4szO3UCthJ2yZvxCdg==
"@abp/select2@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-9.0.1.tgz#9f1c5c27e72bef910fdf63fc3167f415d2ba8db8"
integrity sha512-epyYJH2gAOU2UvGn5FD6372hbW3fL7uZIgGpvfgEfa+l6HPdlV9B1OweU2xkONNCoylShAcP/y8qf+bUh8WDEA==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
select2 "^4.0.13"
"@abp/sweetalert2@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.0.0.tgz#71e511117c18b515e5c658e9cc0667770af65d91"
integrity sha512-wxDqoA6RUhkbcN27Kzslu/m2ai5T9AkJRtgs7pV55LvPv1ohuCPEUeTVVh8S/5Pgmr7aPFAPxaaCO32aPcImPw==
"@abp/sweetalert2@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-9.0.1.tgz#a364b21665cb76e92640496dc8a77b8edbce4500"
integrity sha512-0RXR/lzy3qMJLisGUz4qrcA9Clu/Tt1ABGzB64ynG1t/l5XessQsIB5Hkt6MbSyOEsprRsq+ZtKH35CwTRPdAw==
dependencies:
"@abp/core" "~9.0.0"
"@abp/core" "~9.0.1"
sweetalert2 "^11.14.1"
"@abp/timeago@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.0.0.tgz#8f262ba2a74e1c133d0c129f8a0bc55c29441e1a"
integrity sha512-eaNdIUEtDXnuYqS5O7EGRV+j6XOLAsxz7gd5BIKR9lDujd8pEjSJkXKO+/0KxLTkUSy3JudIO37K1h2wTpbYZA==
"@abp/timeago@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-9.0.1.tgz#d1b7e11fdc267bdb736ff4b591afd3fb81c07cce"
integrity sha512-dNfUQwilrY+JHxL5lwJ++ixYDGAs5lF1EDxKMQMIXD/ak5L8rkdLns8Otta7FhgDDny9UJ7XWQjYsG8ZxxybRQ==
dependencies:
"@abp/jquery" "~9.0.0"
"@abp/jquery" "~9.0.1"
timeago "^1.6.7"
"@abp/toastr@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-9.0.0.tgz#74d74ecb3e7f2d63b2ff7735c89a40deb3dc3a13"
integrity sha512-n9uJvw8l2HoxeowPO4diEMxwchGh6puefhyRljFAAstzK77Mhn0u2J7kPynNv94rLSYTYoCxUeJ4TTN+/TVDGQ==
"@abp/toastr@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-9.0.1.tgz#2105a639d7cd4fa9c671b4215d719a78fef7e0ee"
integrity sha512-10KuHnSJOxkvzqs6Aim6GJp8xBuT+TsZqBE1KaXQnERPawmOo4+ME28kA/7lq8fvgFUNO3K41T2uuA1GPHVQcg==
dependencies:
"@abp/jquery" "~9.0.0"
"@abp/jquery" "~9.0.1"
toastr "^2.1.4"
"@abp/utils@~9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.0.0.tgz#2264fd83105b061860edd98dbe0efdfe55803ed5"
integrity sha512-/SB4VYk3Av+ltXv6ovkFHG4iWiECLLitaYWcEHxVAO80ufNjrNnWpHFpXHP8u/RpXIp/CoNiHLr6j5TkZUTZjw==
"@abp/utils@~9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-9.0.1.tgz#dccaf7cf92147304d454ffb9a8664d52dc7e5c5a"
integrity sha512-7eOKT0ciKNdtuASck1kifyc4dC6CeebgxV+rx/EAi3YwPMDkqie6YwLGUqKdGW2N/MZ15JuDR6gkuZxKHEb0NQ==
dependencies:
just-compare "^2.3.0"

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

Loading…
Cancel
Save