diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
index c02e6e13de..2548ab8176 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
@@ -91,6 +91,7 @@
"UserNotFound": "User not found",
"{0}WillBeRemovedFromDevelopers": "{0} Will be removed from developers, do you confirm?",
"{0}WillBeRemovedFromOwners": "{0} Will be removed from owners, do you confirm?",
+ "{0}WillBeRemovedFromMembers": "{0} Will be removed from members, do you confirm?",
"Computers": "Computers",
"UniqueComputerId": "Unique computer id",
"LastSeenDate": "Last seen date",
@@ -213,7 +214,26 @@
"Currency": "Currency",
"Gateway": "Gateway",
"State": "State",
- "FailReason": "Fail reason"
-
+ "FailReason": "Fail reason",
+ "ReIndexAllPosts": "Reindex All Posts",
+ "ReIndexAllPostsConfirmationMessage": "Are you sure you want to reindex all posts?",
+ "SuccessfullyReIndexAllPosts": "All posts have been successfully reindexed.",
+ "Permission:FullSearch": "Full text search",
+ "Menu:CliAnalytics": "Cli Analytics",
+ "TemplateName": "Template name",
+ "TemplateVersion": "Template version",
+ "DatabaseProvider": "Database provider",
+ "IsTiered": "Is Tiered",
+ "ProjectName": "Project name",
+ "Username": "User name",
+ "Tool": "Tool",
+ "Command": "Command",
+ "UiFramework": "Ui framework",
+ "Options": "Options",
+ "CliAnalytics": "Cli Analytics",
+ "Permission:CliAnalyticses": "Cli Analyticses",
+ "Permission:CliAnalytics": "Cli Analytics",
+ "Search": "Search",
+ "ClearFilter": "Clear filter"
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json
index 1884b0025f..c193a0b621 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json
@@ -163,6 +163,25 @@
"Enum:ContentSource:2": "Video İçerik",
"DeleteCoverImage": "Kapak Fotoğrafını Sil",
"DeleteCoverImageConfirmationMessage": "Kapak fotoğrafını \"{0}\" isimli makale için silmek istediğinize emin misiniz?",
- "DeleteCoverImageSuccessMessage": "Kapak fotoğrafı başarılı bir şekilde silinmiştir"
+ "DeleteCoverImageSuccessMessage": "Kapak fotoğrafı başarılı bir şekilde silinmiştir",
+ "ReIndexAllPosts": "Yeniden indeksle",
+ "ReIndexAllPostsConfirmationMessage": "Tüm gönderileri yeniden indeksleyeceğinizden emin misiniz?",
+ "SuccessfullyReIndexAllPosts": "Tüm gönderiler başarıyla yeniden indekslendi",
+ "Menu:CliAnalytics": "Cli Analitik",
+ "TemplateName": "Şablon ismi",
+ "TemplateVersion": "Şablon versiyon",
+ "DatabaseProvider": "Veritabanı sağlayıcı",
+ "IsTiered": "Katmanlı mı",
+ "ProjectName": "Proje ismi",
+ "Username": "Kullanıcı adı",
+ "Tool": "Araç",
+ "Command": "Komut",
+ "UiFramework": "Ui çerçevesi",
+ "Options": "Seçenekler",
+ "CliAnalytics": "Cli Analitik",
+ "Permission:CliAnalyticses": "Cli Analitikleri",
+ "Permission:CliAnalytics": "Cli Analitik",
+ "Search": "Ara",
+ "ClearFilter": "Filtreyi temizle"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json
index f5cbeb0213..7351fde5cb 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json
@@ -35,6 +35,7 @@
"NameFilter": "名称",
"CreationTime": "创建时间",
"IsPro": "是否为专业版",
+ "IsFreeToActiveLicenseOwners": "许可证所有者免费",
"ShowOnModuleList": "展示模块列表",
"EfCoreConfigureMethodName": "配置方法法",
"IsProFilter": "是否为专业版",
@@ -90,6 +91,7 @@
"UserNotFound": "用户不存在",
"{0}WillBeRemovedFromDevelopers": "{0} 将从开发者中移除, 你确定吗?",
"{0}WillBeRemovedFromOwners": "{0} 将从所有者中移除, 你确定吗?",
+ "{0}WillBeRemovedFromMembers": "{0} 将从成员中删除, 你确定吗?",
"Computers": "计算机",
"UniqueComputerId": "计算机唯一ID",
"LastSeenDate": "上次查看日期",
@@ -194,6 +196,27 @@
"CoverImage": "封面图片",
"RemoveCacheConfirmationMessage": "你确定要删除\"{0}\" 文章缓存?",
"SuccessfullyRemoved": "清除成功",
- "RemoveCache": "删除缓存"
+ "RemoveCache": "删除缓存",
+ "Language": "语言",
+ "Optional": "可选的",
+ "CreateArticleLanguageInfo": "发帖所用的语言",
+ "Enum:ContentSource:2": "视频发布",
+ "VideoPreview": "视频预览",
+ "VideoPreviewErrorMessage": "无法从YouTube获取给定的视频网址. 可能是由于视频是私有视频,或者给定的URL不可用.",
+ "DeleteCoverImage": "删除封面图片",
+ "DeleteCoverImageConfirmationMessage": "你确定要删除封面图片吗",
+ "DeleteCoverImageSuccessMessage": "成功删除封面图片",
+ "PaymentsOf": "付款的",
+ "ShowPaymentsOfOrganization": "显示付款",
+ "Date": "日期",
+ "Products": "商品",
+ "TotalAmount": "总金额",
+ "Currency": "货币",
+ "Gateway": "网关",
+ "State": "状态",
+ "FailReason": "失败原因",
+ "ReIndexAllPosts": "重新索引所有帖子",
+ "ReIndexAllPostsConfirmationMessage": "你确定要重新索引所有的帖子吗",
+ "SuccessfullyReIndexAllPosts": "成功索引所有的帖子"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
index 9c1e434ac1..82033e5849 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
@@ -136,6 +136,9 @@
"GitHubUserNameValidationMessage": "Your Github username can not include whitespace, please be sure your Github username is correct.",
"PersonalSiteUrlValidationMessage": "Your personal site URL can not include whitespace, please be sure your personal site URL is correct.",
"TwitterUserNameValidationMessage": "Your Twitter username can not include whitespace, please be sure your Twitter username is correct.",
- "LinkedinUrlValidationMessage": "Your Linkedin URL can not include whitespace, please be sure your Linkedin URL is correct."
+ "LinkedinUrlValidationMessage": "Your Linkedin URL can not include whitespace, please be sure your Linkedin URL is correct.",
+ "NoPostsFound": "No posts found!",
+ "SearchInPosts": "Search in posts...",
+ "MinimumSearchContent": "You must enter at least 3 characters!"
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
index 49b819227f..9d7b56f0f3 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
@@ -110,7 +110,34 @@
"VideoContentForm": "视频内容来源",
"GithubPostForm": "Github文章来源",
"ExternalPostForm": "外部文章来源",
- "PostSourceTypeChooses": "我们接受文章的三种来源类型;",
- "Posts": "文章"
+ "HowToPost": "如何发布",
+ "Posts": "文章",
+ "VideoUrl": "视频Url",
+ "GithubArticleUrl": "Github文章Url",
+ "ExternalArticleUrl": "外部文章Url",
+ "CreatePostCoverInfo": "要创建一个有效的帖子,需要添加封面图片. 上传16:9的图片获取最佳视觉效果. 文件最大支持: 1MB",
+ "ThankYouForContribution": "感谢你对ABP社区的贡献",
+ "GithubArticle": "Github文章",
+ "GithubArticleSubmitStepOne": "1. 用Markdown格式在GitHub的任何公共存储库上写一篇文章. 示例",
+ "GithubArticleSubmitStepTwo": "2. 使用表单提交你的文章URL.",
+ "GithubArticleSubmitStepThree": "3. 你的文章将在这个网站中呈现.",
+ "YoutubeVideo": "Youtube视频",
+ "YoutubeVideoSubmitStepOne": "1. 在YouTube上发布你的视频.",
+ "YoutubeVideoSubmitStepTwo": "2. 使用表单提交视频URL.",
+ "YoutubeVideoSubmitStepThree": "3. 访客将能够在这个网站上直接观看你的视频内容.",
+ "ExternalContent": "外部内容",
+ "ExternalContentSubmitStepOne": "1. 在任何公开的平台创建内容(medium, 你自己的博客或者任何你喜欢的).",
+ "ExternalContentSubmitStepTwo": "2. 使用表单提交内容URL.",
+ "ExternalContentSubmitStepThree": "3. 访客被重定向到原网站的内容.",
+ "ChooseYourContentType": "请选择你想要添加内容的方式",
+ "PostContentViaGithub": "我想要使用 GitHub提交markdown格式的文章",
+ "PostContentViaYoutube": "我想要分享我在 Youtube的可用视频",
+ "PostContentViaExternalSource": "我想要添加我在其他平台发布的内容",
+ "GitHubUserNameValidationMessage": "您的Github用户名不能包含空格, 请确认你的Github用户名是正确的.",
+ "PersonalSiteUrlValidationMessage": "你的个人网站URL不能包含空格, 请确定你的个人网站URL是正确的.",
+ "TwitterUserNameValidationMessage": "你的Twitter用户名不能包含空格, 请确认你的Twitter用户名是正确的.",
+ "LinkedinUrlValidationMessage": "你的领英URL不能包含空格, 请确认你的领英URL是正确的",
+ "NoPostsFound": "没有发现帖子",
+ "SearchInPosts": "搜索帖子…"
}
}
\ No newline at end of file
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
index e187a628f2..8185ead8cd 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
@@ -8,6 +8,7 @@
"ProjectName": "项目名称",
"ProjectType": "项目类型",
"DatabaseProvider": "数据库提供者",
+ "DatabaseManagementSystem": "数据库管理系统",
"NTier": "N层",
"IncludeUserInterface": "包含用户界面",
"CreateNow": "现在创建",
@@ -194,7 +195,7 @@
"MultipleUIOptionsExplanation": "核心框架设计为独立与UI,可以和任何类型的UI系统一起使用. 同时提供了多个开箱即用的预构建集成选项.",
"MultipleDBOptionsExplanation": "该框架可以使用任何数据源,并且以下提供程序已得到正式开发和支持;",
"SelectLanguage": "选择语言",
- "LatestArticleOnCommunity": "关于ABP社区的最新文章",
- "Register": "寄存器"
+ "LatestArticleOnCommunity": "关于ABP社区的最新文章",
+ "Register": "寄存器"
}
-}
+}
\ No newline at end of file
diff --git a/docs/en/Background-Jobs-RabbitMq.md b/docs/en/Background-Jobs-RabbitMq.md
index 2ee478ed17..16b8b0981a 100644
--- a/docs/en/Background-Jobs-RabbitMq.md
+++ b/docs/en/Background-Jobs-RabbitMq.md
@@ -66,6 +66,26 @@ Defining multiple connections is allowed. In this case, you can use different co
}
````
+If you need to connect to the RabbitMQ cluster, you can use the `;` character to separate the host names.
+
+**Example: Connect to the RabbitMQ cluster**
+
+```json
+{
+ "RabbitMQ": {
+ "Connections": {
+ "Default": {
+ "HostName": "123.123.123.123;234.234.234.234"
+ }
+ },
+ "EventBus": {
+ "ClientName": "MyClientName",
+ "ExchangeName": "MyExchangeName"
+ }
+ }
+}
+```
+
#### AbpRabbitMqOptions
`AbpRabbitMqOptions` class can be used to configure the connection strings for the RabbitMQ. You can configure this options inside the `ConfigureServices` of your [module](Module-Development-Basics.md).
diff --git a/docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md b/docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md
index 3e8a3e9da2..86f9774c7f 100644
--- a/docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md
+++ b/docs/en/Community-Articles/2020-10-08-How-To-Add-Custom-Property-To-The-User-Entity/How-To-Add-Custom-Property-To-The-User-Entity.md
@@ -78,7 +78,7 @@ public static class CustomizeUserDemoEfCoreEntityExtensionMappings
nameof(AppUser.Title),
(entityBuilder, propertyBuilder) =>
{
- propertyBuilder.IsRequired();
+ propertyBuilder.HasDefaultValue();
propertyBuilder.HasMaxLength(UserConsts.MaxTitleLength);
}
).MapEfCoreProperty(
diff --git a/docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md b/docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md
index 9587a111bf..3d99e1e3cf 100644
--- a/docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md
+++ b/docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md
@@ -90,7 +90,7 @@ You can use any of the [ConnectionFactry](http://rabbitmq.github.io/rabbitmq-dot
**Example: Specify the connection port**
-````csharp
+````json
{
"RabbitMQ": {
"Connections": {
@@ -103,6 +103,26 @@ You can use any of the [ConnectionFactry](http://rabbitmq.github.io/rabbitmq-dot
}
````
+If you need to connect to the RabbitMQ cluster, you can use the `;` character to separate the host names.
+
+**Example: Connect to the RabbitMQ cluster**
+
+```json
+{
+ "RabbitMQ": {
+ "Connections": {
+ "Default": {
+ "HostName": "123.123.123.123;234.234.234.234"
+ }
+ },
+ "EventBus": {
+ "ClientName": "MyClientName",
+ "ExchangeName": "MyExchangeName"
+ }
+ }
+}
+```
+
### The Options Classes
`AbpRabbitMqOptions` and `AbpRabbitMqEventBusOptions` classes can be used to configure the connection strings and event bus options for the RabbitMQ.
diff --git a/docs/en/UI/Angular/HTTP-Requests.md b/docs/en/UI/Angular/HTTP-Requests.md
index ab1ac04097..aeb55578b8 100644
--- a/docs/en/UI/Angular/HTTP-Requests.md
+++ b/docs/en/UI/Angular/HTTP-Requests.md
@@ -27,13 +27,13 @@ An `HttpInterceptor` is able to catch `HttpErrorResponse` and can be used for
## RestService
-ABP core module has a utility service for HTTP requests: `RestService`. Unless explicitly configured otherwise, it catches HTTP errors and dispatches a `RestOccurError` action. This action is then captured by the `ErrorHandler` introduced by the `ThemeSharedModule`. Since you should already import this module in your app, when the `RestService` is used, all HTTP errors get automatically handled by deafult.
+ABP core module has a utility service for HTTP requests: `RestService`. Unless explicitly configured otherwise, it catches HTTP errors and dispatches a `RestOccurError` action. This action is then captured by the `ErrorHandler` introduced by the `ThemeSharedModule`. Since you should already import this module in your app, when the `RestService` is used, all HTTP errors get automatically handled by default.
### Getting Started with RestService
-In order to use the `RestService`, you must inject it in your class as a dependency.
+In order to use the `RestService`, you must inject it in your class as a dependency.
```js
import { RestService } from '@abp/ng.core';
@@ -200,6 +200,101 @@ getSomeCustomHeaderValue() {
}
```
+You may find `Rest.Observe` enum [here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L10).
-You may find `Rest.Observe` enum [here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L10).
+## HTTP Error Handling
+
+When the `RestService` is used, all HTTP errors are automatically handled by `ErrorHandler` which is a service that exposed by the `@abp/ng.theme.shared` package.
+
+### Custom HTTP Error Handler
+
+A custom HTTP error handler can be registered to an injection token named `HTTP_ERROR_HANDLER`. If a custom handler function is registered, the `ErrorHandler` executes that function.
+
+See an example:
+
+```js
+// http-error-handler.ts
+import { ContentProjectionService, PROJECTION_STRATEGY } from '@abp/ng.core';
+import { ToasterService } from '@abp/ng.theme.shared';
+import { HttpErrorResponse } from '@angular/common/http';
+import { Injector } from '@angular/core';
+import { throwError } from 'rxjs';
+import { Error404Component } from './error404/error404.component';
+
+export function handleHttpErrors(injector: Injector, httpError: HttpErrorResponse) {
+ if (httpError.status === 400) {
+ const toaster = injector.get(ToasterService);
+ toaster.error(httpError.error?.error?.message || 'Bad request!', '400');
+ return;
+ }
+
+ if (httpError.status === 404) {
+ const contentProjection = injector.get(ContentProjectionService);
+ contentProjection.projectContent(PROJECTION_STRATEGY.AppendComponentToBody(Error404Component));
+ return;
+ }
+
+ return throwError(httpError);
+}
+
+// app.module.ts
+import { Error404Component } from './error404/error404.component';
+import { handleHttpErrors } from './http-error-handling';
+import { HTTP_ERROR_HANDLER, ... } from '@abp/ng.theme.shared';
+
+@NgModule({
+ // ...
+ providers: [
+ // ...
+ { provide: HTTP_ERROR_HANDLER, useValue: handleHttpErrors }
+ ],
+ declarations: [
+ //...
+ Error404Component],
+})
+export class AppModule {}
+```
+
+In the example above:
+
+ - Created a function named `handleHttpErrors` and defined as value of the `HTTP_ERROR_HANDLER` provider in app.module. After this, the function executes when an HTTP error occurs.
+ - 400 bad request errors is handled. When a 400 error occurs, backend error message will be displayed as shown below:
+
+ 
+
+ - 404 not found errors is handled. When a 404 error occurs, `Error404Component` will be appended to the `` as shown below:
+
+
+
+ - Since `throwError(httpError)` is returned at bottom of the `handleHttpErrors`, the `ErrorHandler` will handle the HTTP errors except 400 and 404 errors.
+
+
+**Note 1:** If you put `return` to next line of handling an error, default error handling will not work for that error.
+
+```js
+export function handleHttpErrors(injector: Injector, httpError: HttpErrorResponse) {
+ if (httpError.status === 403) {
+ // handle 403 errors here
+ return; // put return to skip default error handling
+ }
+}
+```
+
+**Note 2:** If you put `return throwError(httpError)`, default error handling will work.
+ - `throwError` is a function. It can be imported from `rxjs`.
+ - `httpError` is the second parameter of the error handler function which is registered to the `HTTP_ERROR_HANDLER` provider. Type of the `httpError` is `HttpErrorResponse`.
+
+```js
+import { throwError } from 'rxjs';
+
+export function handleHttpErrors(injector: Injector, httpError: HttpErrorResponse) {
+ if (httpError.status === 500) {
+ // handle 500 errors here
+ return;
+ }
+
+ // you can return the throwError(httpError) at bottom of the function to run the default handler of ABP for HTTP errors that you didn't handle above.
+ return throwError(httpError)
+}
+```
diff --git a/docs/en/UI/Angular/Router-Events.md b/docs/en/UI/Angular/Router-Events.md
index 011d405b22..1d36c3a211 100644
--- a/docs/en/UI/Angular/Router-Events.md
+++ b/docs/en/UI/Angular/Router-Events.md
@@ -1,6 +1,6 @@
# Router Events Simplified
-`RouterEvents` is a utility service to provide an easy implementation for one of the most frequent needs in Angular templates: `TrackByFunction`. Please see [this page in Angular docs](https://angular.io/guide/template-syntax#ngfor-with-trackby) for its purpose.
+`RouterEvents` is a utility service for filtering specific router events and reacting to them. Please see [this page in Angular docs](https://angular.io/api/router/Event) for available router events.
diff --git a/docs/en/UI/Angular/Service-Proxies.md b/docs/en/UI/Angular/Service-Proxies.md
index 8f6b1c4d60..3a10f1b6e8 100644
--- a/docs/en/UI/Angular/Service-Proxies.md
+++ b/docs/en/UI/Angular/Service-Proxies.md
@@ -137,3 +137,7 @@ export class BookComponent implements OnInit {
```
> Please [see this article](https://github.com/abpframework/abp/blob/dev/docs/en/Blog-Posts/2020-09-07%20Angular-Service-Proxies/POST.md) to learn more about service proxies.
+
+### Known Limitations
+
+When you run a project on Visual Studio using IIS Express as the web server, there will be no remote access to your endpoints. This is the default behavior of IIS Express since it explicitly protects you from the security risks of running over the network. However, that will cause the proxy generator to fail because it needs a response from the `/api/abp/api-definition` endpoint. You may serve your endpoints via Kestrel to avoid this. Running `dotnet run` in your command line (at your project folder) will do that for you.
diff --git a/docs/en/UI/Angular/images/custom-error-handler-404-component.jpg b/docs/en/UI/Angular/images/custom-error-handler-404-component.jpg
new file mode 100644
index 0000000000..e4b2f15c50
Binary files /dev/null and b/docs/en/UI/Angular/images/custom-error-handler-404-component.jpg differ
diff --git a/docs/en/UI/Angular/images/custom-error-handler-toaster-message.jpg b/docs/en/UI/Angular/images/custom-error-handler-toaster-message.jpg
new file mode 100644
index 0000000000..c1cced8237
Binary files /dev/null and b/docs/en/UI/Angular/images/custom-error-handler-toaster-message.jpg differ
diff --git a/docs/zh-Hans/Background-Jobs-RabbitMq.md b/docs/zh-Hans/Background-Jobs-RabbitMq.md
index 979beec872..0f9fb21d8a 100644
--- a/docs/zh-Hans/Background-Jobs-RabbitMq.md
+++ b/docs/zh-Hans/Background-Jobs-RabbitMq.md
@@ -1,3 +1,153 @@
-# RabbitMQ Background Job Manager
+# RabbitMQ 后台作业管理
-待添加
\ No newline at end of file
+RabbitMQ 是一个标准的消息队列中间件,虽然它常用于消息传递/分布式事件,但也非常适合存储 FIFO(先进先出) 顺序的后台作业.
+
+ABP Framework 提供了 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包,将使用 RabbitMQ 来执行后台作业.
+
+> 参阅 [后台作业文档](Background-Jobs.md) 学习如何使用后台作业系统,本文只介绍了如何安装和配置 RabbitMQ 集成.
+
+## 安装
+
+使用 ABP CLI 将 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包添加到你的项目:
+
+- 如果之前没有安装过 [ABP CLI](https://docs.abp.io/en/abp/latest/CLI),请先安装它.
+- 跳转到待安装后台作业管理的项目目录中(包含 `.csproj` 文件的目录),打开终端管理器.
+- 执行 `abp add-package Volo.Abp.BackgroundJobs.RabbitMQ` 命令.
+
+如果你想要手动安装,请先用 NuGet 包管理器安装 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包到指定项目,之后使在你的 [模块](Module-Development-Basics.md) 上面添加 `[DependsOn(typeof(AbpBackgroundJobsRabbitMqModule))]` 配置依赖.
+
+## 配置
+
+### 默认配置
+
+默认配置将会使用标准端口和主机名(localhost)连接到 RabbitMQ 服务,**你不需要进行额外配置**.
+
+### RabbitMQ 连接
+
+你可以使用 ASP.NET Core 的 [标准配置系统](Configuration.md) 对 RabbitMQ 进行详细配置,比如 `appsettings.json` 或者是 [选项类](Options.md).
+
+#### 通过 `appsettings.json` 文件配置
+
+这种方式是配置 RabbitMQ 连接最简单的方式,你可以使用其他的配置源(例如环境变量).这些强大的功能都是由 [ASP.NET Core](https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/) 提供的支持.
+
+**示例: 配置默认的 RabbitMQ 连接**
+
+```json
+{
+ "RabbitMQ": {
+ "Connections": {
+ "Default": {
+ "HostName": "123.123.123.123",
+ "Port": "5672"
+ }
+ }
+ }
+}
+```
+
+你可以在配置文件使用所有 [ConnectionFactry](http://rabbitmq.github.io/rabbitmq-dotnet-client/api/RabbitMQ.Client.ConnectionFactory.html#properties) 的属性,关于这些属性的具体含义,可以查看 RabbitMQ 的 [官方文档](https://www.rabbitmq.com/dotnet-api-guide.html#exchanges-and-queues).
+
+目前我们允许定义多个连接,多连接的情况适用于不同的后台作业,具体配置信息可以参考下面的 RabbitMQ 后台作业配置说明.
+
+**示例: 定义两个 RabbitMQ 连接**
+
+```json
+{
+ "RabbitMQ": {
+ "Connections": {
+ "Default": {
+ "HostName": "123.123.123.123"
+ },
+ "SecondConnection": {
+ "HostName": "321.321.321.321"
+ }
+ }
+ }
+}
+```
+
+如果需要连接到 RabbitMQ 集群,你可以指定多个 HostName.
+
+**示例: 连接到 RabbitMQ 集群**
+
+```json
+{
+ "RabbitMQ": {
+ "Connections": {
+ "Default": {
+ "HostName": "123.123.123.123;234.234.234.234"
+ }
+ },
+ "EventBus": {
+ "ClientName": "MyClientName",
+ "ExchangeName": "MyExchangeName"
+ }
+ }
+}
+```
+
+#### 使用选项类
+
+`AbpRabbitMqOptions` 类型用于配置 RabbitMQ 的连接字符串,你可以在 [模块](Module-Development-Basics.md) 的 `ConfigureService` 方法中进行配置.
+
+**示例: 配置 RabbitMQ 连接**
+
+```csharp
+Configure(options =>
+{
+ options.Connections.Default.UserName = "user";
+ options.Connections.Default.Password = "pass";
+ options.Connections.Default.HostName = "123.123.123.123";
+ options.Connections.Default.Port = 5672;
+});
+```
+
+关于选项类,可以结合 `appsettings.json` 文件一起使用.针对同一个属性,在选项类里面对该值进行了设定,会覆盖掉 `appsettings.json` 的值.
+
+### RabbitMQ 后台作业配置说明
+
+#### 后台作业队列的名称
+
+默认情况下,每个后台作业都会使用一个单独的队列,结合标准前缀和作业名称来构造一个完整的队列名称.默认的前缀为 `AbpBackgroundJobs`,所以有一个作业的名称是 `EmailSending` 的话,在 RabbitMQ 的队列名称就是 `AbpBackgroundJobs.EmailSending`.
+
+> 在后台作业的参数类上,可以使用 `BackgroundJobName` 特性指定后台作业的名称.否则的话,后台作业的名称将会是后台作业类的全名(也包含命名空间).
+
+#### 后台作业使用的连接
+
+默认情况下,后台作业都会使用 `Default` 作为默认连接.
+
+#### 自定义
+
+`AbpRabbitMqBackgroundJobOptions` 可以自定义队列名和作业使用的 RabbitMQ 连接.
+
+**示例: **
+
+```csharp
+Configure(options =>
+{
+ options.DefaultQueueNamePrefix = "my_app_jobs.";
+ options.JobQueues[typeof(EmailSendingArgs)] =
+ new JobQueueConfiguration(
+ typeof(EmailSendingArgs),
+ queueName: "my_app_jobs.emails",
+ connectionName: "SecondConnection"
+ );
+});
+```
+
+- 这个示例将默认的队列名前缀设置为 `my_app_jobs.`,如果多个项目都使用的同一个 RabbitMQ 服务,设置不同的前缀可以避免执行其他项目的后台作业.
+- 这里还设置了 `EmailSendingArgs` 绑定的 RabbitMQ 连接.
+
+`JobQueueConfiguration` 类的构造函数中,还有一些其他的可选参数.
+
+- `queueName`: 指定后台作业对应的队列名称(全名).
+- `connectionName`: 后台作业对应的 RabbitMQ 连接名称,默认是 `Default`.
+- `durable`: 可选参数,默认为 `true`.
+- `exclusive`: 可选参数,默认为 `false`.
+- `autoDelete`: 可选参数,默认为 `false`.
+
+如果你想要更多地了解 `durable`,`exclusive`,`autoDelete` 的用法,请阅读 RabbitMQ 提供的文档.
+
+## 另请参阅
+
+- [后台作业](Background-Jobs.md)
diff --git a/docs/zh-Hans/Distributed-Event-Bus-RabbitMQ-Integration.md b/docs/zh-Hans/Distributed-Event-Bus-RabbitMQ-Integration.md
index 2a1810e309..ab7cb68bd7 100644
--- a/docs/zh-Hans/Distributed-Event-Bus-RabbitMQ-Integration.md
+++ b/docs/zh-Hans/Distributed-Event-Bus-RabbitMQ-Integration.md
@@ -103,6 +103,26 @@
}
````
+如果需要连接到 RabbitMQ 集群,你可以指定多个 HostName。
+
+**示例: 连接到 RabbitMQ 集群**
+
+```json
+{
+ "RabbitMQ": {
+ "Connections": {
+ "Default": {
+ "HostName": "123.123.123.123;234.234.234.234"
+ }
+ },
+ "EventBus": {
+ "ClientName": "MyClientName",
+ "ExchangeName": "MyExchangeName"
+ }
+ }
+}
+```
+
### 选项类
`AbpRabbitMqOptions` 和 `AbpRabbitMqEventBusOptions` 类用于配置RabbitMQ的连接字符串和事件总线选项.
diff --git a/docs/zh-Hans/Repositories.md b/docs/zh-Hans/Repositories.md
index 79186b79da..857df51439 100644
--- a/docs/zh-Hans/Repositories.md
+++ b/docs/zh-Hans/Repositories.md
@@ -150,7 +150,7 @@ var people = _personRepository
* 这里异步方法**不是标准LINQ方法**,它们定义在[Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore)Nuget包中.
* 标准模板应用层与领域层**不引用**EF Core 包以实现数据库提供程序独立.
-根据你的需求和开发模式,你可以根据以下选项使用异步方法.s
+根据你的需求和开发模式,你可以根据以下选项使用异步方法.
> 强烈建议使用异步方法! 在执行数据库查询时不要使用同步LINQ方法,以便能够开发可伸缩的应用程序.
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/TuiEditor/TuiEditorScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/TuiEditor/TuiEditorScriptContributor.cs
index e788c612cf..e2e826445f 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/TuiEditor/TuiEditorScriptContributor.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/TuiEditor/TuiEditorScriptContributor.cs
@@ -16,6 +16,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.TuiEditor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
+ context.Files.AddIfNotContains("/libs/tui-editor/tui-editor-jquery-patch.js");
context.Files.AddIfNotContains("/libs/to-mark/to-mark.min.js");
if (context.FileProvider.GetFileInfo("/libs/tui-code-snippet/tui-code-snippet.min.js").Exists)
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs
index ae09573d60..1a017e9925 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs
@@ -3,6 +3,7 @@ using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
+using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Options;
@@ -19,7 +20,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring
public AbpRemoteServiceApiDescriptionProvider(
IModelMetadataProvider modelMetadataProvider,
- IOptions mvcOptionsAccessor,
+ IOptions mvcOptionsAccessor,
IOptions optionsAccessor)
{
_modelMetadataProvider = modelMetadataProvider;
@@ -41,7 +42,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring
{
foreach (var apiResponseType in GetApiResponseTypes())
{
- foreach (var result in context.Results.Where(x => IsRemoteService(x.ActionDescriptor)))
+ foreach (var result in context.Results.Where(IsRemoteService))
{
var actionProducesResponseTypeAttributes =
ReflectionHelper.GetAttributesOfMemberOrDeclaringType(
@@ -62,7 +63,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring
foreach (var apiResponse in _options.SupportedResponseTypes)
{
apiResponse.ModelMetadata = _modelMetadataProvider.GetMetadataForType(apiResponse.Type);
-
+
foreach (var responseTypeMetadataProvider in _mvcOptions.OutputFormatters.OfType())
{
var formatterSupportedContentTypes = responseTypeMetadataProvider.GetSupportedContentTypes(null, apiResponse.Type);
@@ -85,10 +86,29 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring
return _options.SupportedResponseTypes;
}
- protected virtual bool IsRemoteService(ActionDescriptor actionDescriptor)
+ protected virtual bool IsRemoteService(ApiDescription actionDescriptor)
{
- var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(actionDescriptor.GetMethodInfo());
- return remoteServiceAttr != null && remoteServiceAttr.IsEnabled;
+ if (actionDescriptor.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
+ {
+ var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(controllerActionDescriptor.MethodInfo);
+ if (remoteServiceAttr != null && remoteServiceAttr.IsEnabled)
+ {
+ return true;
+ }
+
+ remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(controllerActionDescriptor.ControllerTypeInfo);
+ if (remoteServiceAttr != null && remoteServiceAttr.IsEnabled)
+ {
+ return true;
+ }
+
+ if (typeof(IRemoteService).IsAssignableFrom(controllerActionDescriptor.ControllerTypeInfo))
+ {
+ return true;
+ }
+ }
+
+ return false;
}
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/Volo.Abp.Authorization/Microsoft/AspNetCore/Authorization/AbpAuthorizationServiceExtensions.cs b/framework/src/Volo.Abp.Authorization/Microsoft/AspNetCore/Authorization/AbpAuthorizationServiceExtensions.cs
index bb820c3a83..133209f30a 100644
--- a/framework/src/Volo.Abp.Authorization/Microsoft/AspNetCore/Authorization/AbpAuthorizationServiceExtensions.cs
+++ b/framework/src/Volo.Abp.Authorization/Microsoft/AspNetCore/Authorization/AbpAuthorizationServiceExtensions.cs
@@ -65,6 +65,23 @@ namespace Microsoft.AspNetCore.Authorization
{
return (await authorizationService.AuthorizeAsync(policyName)).Succeeded;
}
+
+ public static async Task IsGrantedAnyAsync(
+ this IAuthorizationService authorizationService,
+ params string[] policyNames)
+ {
+ Check.NotNullOrEmpty(policyNames, nameof(policyNames));
+
+ foreach (var policyName in policyNames)
+ {
+ if ((await authorizationService.AuthorizeAsync(policyName)).Succeeded)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
public static async Task IsGrantedAsync(this IAuthorizationService authorizationService, object resource, IAuthorizationRequirement requirement)
{
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ModuleProjectBuildPipelineBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ModuleProjectBuildPipelineBuilder.cs
index 9b66f17834..d4a74d9364 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ModuleProjectBuildPipelineBuilder.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ModuleProjectBuildPipelineBuilder.cs
@@ -1,4 +1,5 @@
using Volo.Abp.Cli.ProjectBuilding.Building.Steps;
+using Volo.Abp.Cli.ProjectBuilding.Templates;
namespace Volo.Abp.Cli.ProjectBuilding.Building
{
@@ -12,6 +13,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building
pipeline.Steps.Add(new ProjectReferenceReplaceStep());
pipeline.Steps.Add(new ReplaceCommonPropsStep());
pipeline.Steps.Add(new ReplaceConfigureAwaitPropsStep());
+ pipeline.Steps.Add(new UpdateNuGetConfigStep("/NuGet.Config"));
pipeline.Steps.Add(new CreateProjectResultZipStep());
return pipeline;
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/MicroserviceServiceRandomPortStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/MicroserviceServiceRandomPortStep.cs
new file mode 100644
index 0000000000..88a19b43af
--- /dev/null
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/MicroserviceServiceRandomPortStep.cs
@@ -0,0 +1,78 @@
+using System;
+using System.IO;
+using System.Linq;
+using Volo.Abp.Cli.Commands;
+
+namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
+{
+ public class MicroserviceServiceRandomPortStep : ProjectBuildPipelineStep
+ {
+ private readonly string _defaultPort = string.Empty;
+ private string _tyeFileContent = null;
+
+ public MicroserviceServiceRandomPortStep(string defaultPort)
+ {
+ _defaultPort = defaultPort;
+ }
+
+ public override void Execute(ProjectBuildContext context)
+ {
+ var newPort = GetNewRandomPort(context);
+
+ var targetFiles = context.Files.Where(f=> f.Name.EndsWith("launchSettings.json") || f.Name.EndsWith("appsettings.json")).ToList();
+
+ foreach (var file in targetFiles)
+ {
+ file.SetContent(file.Content.Replace(_defaultPort, newPort));
+ }
+ }
+
+ private string GetNewRandomPort(ProjectBuildContext context)
+ {
+ string newPort;
+ var rnd = new Random();
+ var tryCount = 0;
+
+ do
+ {
+ newPort = rnd.Next(44350, 45350).ToString();
+
+ if (tryCount++ > 2000)
+ {
+ break;
+ }
+
+ } while (PortExistsForAnotherService(context, newPort));
+
+ return newPort;
+ }
+
+ private bool PortExistsForAnotherService(ProjectBuildContext context, string newPort)
+ {
+ return ReadTyeFileContent(context).SplitToLines().Any(l => l.Contains("port") && l.Contains(newPort));
+ }
+
+ private string ReadTyeFileContent(ProjectBuildContext context)
+ {
+ if (_tyeFileContent != null)
+ {
+ return _tyeFileContent;
+ }
+
+ var solutionFolderPath = context.BuildArgs.ExtraProperties[NewCommand.Options.OutputFolder.Short] ??
+ context.BuildArgs.ExtraProperties[NewCommand.Options.OutputFolder.Long] ??
+ Directory.GetCurrentDirectory();
+
+ var tyeFilePath = Path.Combine(solutionFolderPath, "tye.yaml");
+
+ if (!File.Exists(tyeFilePath))
+ {
+ return String.Empty;
+ }
+
+ _tyeFileContent = File.ReadAllText(tyeFilePath);
+
+ return _tyeFileContent;
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveCmsKitStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveCmsKitStep.cs
index 789f9e47cb..a0778d5a4d 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveCmsKitStep.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveCmsKitStep.cs
@@ -9,12 +9,9 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{
var commonFiles = context.Files.Where(f =>
f.Name.EndsWith(".csproj") ||
- f.Name.EndsWith("Module.cs") ||
- f.Name.EndsWith("MyProjectNameMigrationsDbContext.cs") ||
- f.Name.EndsWith("MyProjectNameMigrationsDbContextBase.cs") ||
- f.Name.EndsWith("MyProjectNameGlobalFeatureConfigurator.cs") ||
- (f.Name.EndsWith(".cshtml") && f.Name.Contains("MyCompanyName.MyProjectName.Web.Public"))
- );
+ f.Name.EndsWith(".cs") ||
+ f.Name.EndsWith(".json") ||
+ f.Name.EndsWith(".cshtml"));
foreach (var file in commonFiles)
{
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveEfCoreDependencyFromPublicStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveEfCoreDependencyFromPublicStep.cs
index f9e906c902..1f7b8f4e6f 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveEfCoreDependencyFromPublicStep.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveEfCoreDependencyFromPublicStep.cs
@@ -7,8 +7,13 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{
public override void Execute(ProjectBuildContext context)
{
- context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.Web.Public.csproj"))?.RemoveTemplateCodeIfNot("EFCORE");
- context.Files.FirstOrDefault(f => f.Name.EndsWith("MyProjectNameWebPublicModule.cs"))?.RemoveTemplateCodeIfNot("EFCORE");
+ foreach (var file in context.Files)
+ {
+ if (file.Name.EndsWith(".cs") || file.Name.EndsWith(".csproj") || file.Name.EndsWith(".json"))
+ {
+ file.RemoveTemplateCodeIfNot("EFCORE");
+ }
+ }
}
}
}
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveGlobalFeaturesPackageStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveGlobalFeaturesPackageStep.cs
index 06fd565353..15a84576c6 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveGlobalFeaturesPackageStep.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveGlobalFeaturesPackageStep.cs
@@ -7,8 +7,15 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{
public override void Execute(ProjectBuildContext context)
{
- context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.Domain.Shared.csproj"))?.RemoveTemplateCodeIf("CMS-KIT");
- context.Files.FirstOrDefault(f => f.Name.EndsWith("MyProjectNameDomainSharedModule.cs"))?.RemoveTemplateCodeIf("CMS-KIT");
+ var commonFiles = context.Files.Where(f =>
+ f.Name.EndsWith(".csproj") ||
+ f.Name.EndsWith(".cs") ||
+ f.Name.EndsWith(".cshtml"));
+
+ foreach (var file in commonFiles)
+ {
+ file.RemoveTemplateCodeIf("CMS-KIT");
+ }
}
}
}
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveProjectFromTyeStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveProjectFromTyeStep.cs
new file mode 100644
index 0000000000..1c51e22bc9
--- /dev/null
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveProjectFromTyeStep.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
+{
+ public class RemoveProjectFromTyeStep : ProjectBuildPipelineStep
+ {
+ private readonly string _name;
+
+ public RemoveProjectFromTyeStep(string name)
+ {
+ _name = name;
+ }
+
+ public override void Execute(ProjectBuildContext context)
+ {
+ var tyeFile = context.Files.FirstOrDefault(f => f.Name == "/tye.yaml");
+
+ if (tyeFile == null)
+ {
+ return;
+ }
+
+ var lines = tyeFile.GetLines();
+ var newLines = new List();
+
+ var nameLine = $"- name:";
+ var isOneOfTargetLines = false;
+
+ foreach (var line in lines)
+ {
+ if (line.Equals($"{nameLine} {_name}"))
+ {
+ isOneOfTargetLines = true;
+ continue;
+ }
+
+ if (line.StartsWith(nameLine))
+ {
+ isOneOfTargetLines = false;
+ }
+
+ if (!isOneOfTargetLines)
+ {
+ newLines.Add(line);
+ }
+ }
+
+ tyeFile.SetContent(String.Join(Environment.NewLine, newLines));
+ }
+
+ }
+}
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemovePublicRedisStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemovePublicRedisStep.cs
index 1299f5b15d..eced4fd3e5 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemovePublicRedisStep.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemovePublicRedisStep.cs
@@ -8,16 +8,12 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{
public override void Execute(ProjectBuildContext context)
{
- context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.Web.csproj"))?.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
- context.Files.FirstOrDefault(f => f.Name.EndsWith("MyProjectNameWebModule.cs"))?.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
- context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj"))?.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
- context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.HttpApi.Host.csproj"))?.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
- context.Files.FirstOrDefault(f => f.Name.EndsWith("MyProjectNameHttpApiHostModule.cs"))?.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
-
- var appSettingsFiles = context.Files.Where(f => f.Name.EndsWith("appSettings.json", StringComparison.InvariantCultureIgnoreCase)).ToList();
- foreach (var appSettings in appSettingsFiles)
+ foreach (var file in context.Files)
{
- appSettings.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
+ if (file.Name.EndsWith(".cs") || file.Name.EndsWith(".csproj") || file.Name.EndsWith(".json"))
+ {
+ file.RemoveTemplateCodeIfNot("PUBLIC-REDIS");
+ }
}
}
}
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/SolutionRenameStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/SolutionRenameStep.cs
index 27199f6ce4..1ea30fe783 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/SolutionRenameStep.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/SolutionRenameStep.cs
@@ -18,6 +18,14 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
context.BuildArgs.SolutionName.CompanyName,
context.BuildArgs.SolutionName.ProjectName
).Run();
+
+ new SolutionRenamer(
+ context.Files,
+ null,
+ "MyProjectName",
+ null,
+ SolutionName.Parse(context.BuildArgs.SolutionName.CompanyName).ProjectName
+ ).Run();
}
else
{
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryExtensions.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryExtensions.cs
index 91180206bf..34200d41ac 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryExtensions.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryExtensions.cs
@@ -28,6 +28,11 @@ namespace Volo.Abp.Cli.ProjectBuilding.Files
public static void RemoveTemplateCodeMarkers(this FileEntry file)
{
+ if (!file.Content.Contains(""))
+ {
+ return;
+ }
+
file.NormalizeLineEndings();
var lines = file.GetLines();
@@ -59,6 +64,11 @@ namespace Volo.Abp.Cli.ProjectBuilding.Files
private static void RemoveMarkedTemplateCode(this FileEntry file, string beginMark)
{
+ if (!file.Content.Contains(""))
+ {
+ return;
+ }
+
file.NormalizeLineEndings();
var lines = file.GetLines();
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceServiceTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceServiceTemplateBase.cs
index de589336b0..54ec4894b9 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceServiceTemplateBase.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceServiceTemplateBase.cs
@@ -31,6 +31,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
var steps = new List();
DeleteUnrelatedUiProject(context, steps);
+ SetRandomPortForHostProject(context, steps);
RandomizeStringEncryption(context, steps);
return steps;
@@ -54,6 +55,11 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
}
}
+ private static void SetRandomPortForHostProject(ProjectBuildContext context, List steps)
+ {
+ steps.Add(new MicroserviceServiceRandomPortStep("44371"));
+ }
+
private static void RandomizeStringEncryption(ProjectBuildContext context, List steps)
{
steps.Add(new RandomizeStringEncryptionStep());
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs
index 0a56f5272a..1efbe09ff1 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs
@@ -36,9 +36,13 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web",null,
"/applications/web/src/MyCompanyName.MyProjectName.Web"));
steps.Add(new RemoveFolderStep("/applications/web"));
+ steps.Add(new RemoveProjectFromTyeStep("web"));
+
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor",null,
"/applications/blazor/src/MyCompanyName.MyProjectName.Blazor"));
steps.Add(new RemoveFolderStep("/applications/blazor"));
+ steps.Add(new RemoveProjectFromTyeStep("blazor"));
+
steps.Add(new RemoveFolderStep("/angular"));
break;
@@ -46,9 +50,12 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web",null,
"/applications/web/src/MyCompanyName.MyProjectName.Web"));
steps.Add(new RemoveFolderStep("/applications/web"));
+ steps.Add(new RemoveProjectFromTyeStep("web"));
+
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor",null,
"/applications/blazor/src/MyCompanyName.MyProjectName.Blazor"));
steps.Add(new RemoveFolderStep("/applications/blazor"));
+ steps.Add(new RemoveProjectFromTyeStep("blazor"));
break;
case UiFramework.Blazor:
@@ -56,6 +63,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
"/applications/web/src/MyCompanyName.MyProjectName.Web"));
steps.Add(new RemoveFolderStep("/applications/web"));
steps.Add(new RemoveFolderStep("/angular"));
+ steps.Add(new RemoveProjectFromTyeStep("web"));
break;
case UiFramework.Mvc:
@@ -63,6 +71,8 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor",null,
"/applications/blazor/src/MyCompanyName.MyProjectName.Blazor"));
steps.Add(new RemoveFolderStep("/applications/blazor"));
+ steps.Add(new RemoveProjectFromTyeStep("blazor"));
+
steps.Add(new RemoveFolderStep("/angular"));
break;
}
diff --git a/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionLoggingExtensions.cs b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionLoggingExtensions.cs
new file mode 100644
index 0000000000..e31e7cbf51
--- /dev/null
+++ b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionLoggingExtensions.cs
@@ -0,0 +1,13 @@
+using Microsoft.Extensions.Logging;
+using Volo.Abp.Logging;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ public static class ServiceCollectionLoggingExtensions
+ {
+ public static ILogger GetInitLogger(this IServiceCollection services)
+ {
+ return services.GetSingletonInstance().Create();
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs b/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs
index f272484611..5fe47112ba 100644
--- a/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs
+++ b/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs
@@ -260,8 +260,9 @@ namespace System
///
/// String to convert
/// set true to use current culture. Otherwise, invariant culture will be used.
+ /// set true to if you want to convert 'XYZ' to 'xyz'.
/// camelCase of the string
- public static string ToCamelCase(this string str, bool useCurrentCulture = false)
+ public static string ToCamelCase(this string str, bool useCurrentCulture = false, bool handleAbbreviations = false)
{
if (string.IsNullOrWhiteSpace(str))
{
@@ -273,6 +274,11 @@ namespace System
return useCurrentCulture ? str.ToLower() : str.ToLowerInvariant();
}
+ if (handleAbbreviations && IsAllUpperCase(str))
+ {
+ return useCurrentCulture ? str.ToLower() : str.ToLowerInvariant();
+ }
+
return (useCurrentCulture ? char.ToLower(str[0]) : char.ToLowerInvariant(str[0])) + str.Substring(1);
}
@@ -545,5 +551,18 @@ namespace System
return encoding.GetBytes(str);
}
+
+ private static bool IsAllUpperCase(string input)
+ {
+ for (int i = 0; i < input.Length; i++)
+ {
+ if (Char.IsLetter(input[i]) && !Char.IsUpper(input[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
}
}
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
index 1e5b251ca0..61cee2ce90 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
@@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal;
+using Volo.Abp.Logging;
using Volo.Abp.Modularity;
namespace Volo.Abp
@@ -60,7 +62,7 @@ namespace Volo.Abp
{
//TODO: Shutdown if not done before?
}
-
+
protected virtual void SetServiceProvider(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
@@ -71,12 +73,31 @@ namespace Volo.Abp
{
using (var scope = ServiceProvider.CreateScope())
{
+ WriteInitLogs(scope.ServiceProvider);
scope.ServiceProvider
.GetRequiredService()
.InitializeModules(new ApplicationInitializationContext(scope.ServiceProvider));
}
}
+ protected virtual void WriteInitLogs(IServiceProvider serviceProvider)
+ {
+ var logger = serviceProvider.GetService>();
+ if (logger == null)
+ {
+ return;
+ }
+
+ var initLogger = serviceProvider.GetRequiredService().Create();
+
+ foreach (var entry in initLogger.Entries)
+ {
+ logger.Log(entry.LogLevel, entry.EventId, entry.State, entry.Exception, entry.Formatter);
+ }
+
+ initLogger.Entries.Clear();
+ }
+
protected virtual IReadOnlyList LoadModules(IServiceCollection services, AbpApplicationCreationOptions options)
{
return services
@@ -87,7 +108,7 @@ namespace Volo.Abp
options.PlugInSources
);
}
-
+
//TODO: We can extract a new class for this
protected virtual void ConfigureServices()
{
@@ -158,4 +179,4 @@ namespace Volo.Abp
}
}
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs
index 83a7ec163e..ee06003204 100644
--- a/framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs
@@ -1,6 +1,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using Volo.Abp.Logging;
using Volo.Abp.Modularity;
using Volo.Abp.Reflection;
@@ -16,7 +17,7 @@ namespace Volo.Abp.Internal
}
internal static void AddCoreAbpServices(this IServiceCollection services,
- IAbpApplication abpApplication,
+ IAbpApplication abpApplication,
AbpApplicationCreationOptions applicationCreationOptions)
{
var moduleLoader = new ModuleLoader();
@@ -35,6 +36,7 @@ namespace Volo.Abp.Internal
services.TryAddSingleton(moduleLoader);
services.TryAddSingleton(assemblyFinder);
services.TryAddSingleton(typeFinder);
+ services.TryAddSingleton(new DefaultInitLoggerFactory());
services.AddAssemblyOf();
@@ -47,4 +49,4 @@ namespace Volo.Abp.Internal
});
}
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Logging/AbpInitLogEntry.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Logging/AbpInitLogEntry.cs
new file mode 100644
index 0000000000..da44f2be2b
--- /dev/null
+++ b/framework/src/Volo.Abp.Core/Volo/Abp/Logging/AbpInitLogEntry.cs
@@ -0,0 +1,20 @@
+using System;
+using Microsoft.Extensions.Logging;
+
+namespace Volo.Abp.Logging
+{
+ public class AbpInitLogEntry
+ {
+ public LogLevel LogLevel { get; set; }
+
+ public EventId EventId { get; set; }
+
+ public object State { get; set; }
+
+ public Exception Exception { get; set; }
+
+ public Func