diff --git a/.github/workflows/angular.yml b/.github/workflows/angular.yml index 60051c2dc1..1b4d37bd39 100644 --- a/.github/workflows/angular.yml +++ b/.github/workflows/angular.yml @@ -3,6 +3,7 @@ on: pull_request: paths: - 'npm/ng-packs/**' + - '!npm/ng-packs/scripts/**' jobs: build-test-lint: runs-on: ubuntu-18.04 diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json index dbe786fb97..de3cbc8e2d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json @@ -23,7 +23,7 @@ "UrlContentNotFound": "Url content not found.", "Summary": "Summary", "MostRead": "Most Read", - "LatestArticles": "Latest Articles", + "Latest": "Latest", "ContributeAbpCommunity": "Contribute to the ABP Community", "SubmitYourArticle": "Submit Your Article", "ContributionGuide": "Contribution Guide", @@ -36,6 +36,28 @@ "FeatureRequest": "Feature Request", "CreateArticleTitleInfo": "Title of the article to be shown on the article list.", "CreateArticleUrlInfo": "Original GitHub/External URL of the article.", - "CreateArticleSummaryInfo": "A short summary of the article to be shown on the article list." + "CreateArticleSummaryInfo": "A short summary of the article to be shown on the article list.", + "CreateArticleCoverInfo": "For creating an effective article, add a cover photo. Only 16:9 aspect ratio pictures will be accepted!", + "ThisExtensionIsNotAllowed": "This extension is not allowed.", + "TheFileIsTooLarge": "The file is too large.", + "GoToTheArticle": "Go to the Article", + "Contribute": "Contribute", + "OverallProgress": "Overall Progress", + "Done": "Done", + "Open": "Open", + "Closed": "Closed", + "LatestQuestionOnThe": "Latest Question On The", + "Stackoverflow": "Stackoverflow", + "Votes": "votes", + "Answer": "Answer", + "Views": "views", + "Answered": "Answered", + "WaitingForYourAnswer": "Waiting for your answer", + "Asked": "asked", + "AllQuestions": "All Questions", + "NextVersion": "Next Version", + "MilestoneErrorMessage": "Could not get the current milestone details from Github.", + "QuestionItemErrorMessage": "Could not get the latest question details from Stackoverflow.", + "Oops": "Oops!" } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json new file mode 100644 index 0000000000..307ff066a7 --- /dev/null +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json @@ -0,0 +1,64 @@ +{ + "culture": "zh-Hans", + "texts": { + "Permission:CommunityArticle": "社区文章", + "Permission:Edit": "修改", + "Waiting": "等待中", + "Approved": "已批准", + "Rejected": "已拒绝", + "Wait": "等待", + "Approve": "批准", + "Reject": "拒绝", + "ReadArticle": "阅读文章", + "Status": "状态", + "ContentSource": "内容来源", + "Details": "详情", + "Url": "url", + "Title": "标题", + "CreationTime": "创建时间", + "Save": "保存", + "SameUrlAlreadyExist": "url已存在,如果你想要添加这篇文章,你需要更改url!", + "UrlIsNotValid": "Url无效.", + "UrlNotFound" : "Url未找到.", + "UrlContentNotFound": "Url内容未找到.", + "Summary": "摘要", + "MostRead": "阅读最多", + "Latest": "最新", + "ContributeAbpCommunity": "为ABP社区做贡献", + "SubmitYourArticle": "提交你的文章", + "ContributionGuide": "贡献指南", + "BugReport": "Bug报告", + "SeeAllArticles": "查看所有的文章", + "WelcomeToABPCommunity!": "欢迎来到ABP社区!", + "MyProfile": "我的资料", + "MyOrganizations": "我的组织", + "EmailNotValid": "请输入有效的电子邮箱地址.", + "FeatureRequest": "功能请求", + "CreateArticleTitleInfo": "文章标题显示在文章列表中.", + "CreateArticleUrlInfo": "文章的原始GitHub/外部URL.", + "CreateArticleSummaryInfo": "文章的简短摘要将显示在文章列表中.", + "CreateArticleCoverInfo": "为了创建有效的文章,请添加封面图. 仅支持16:9的图片!", + "ThisExtensionIsNotAllowed": "不允许此扩展名.", + "TheFileIsTooLarge": "文件过大.", + "GoToTheArticle": "转到文章", + "Contribute": "贡献", + "OverallProgress": "总体流程", + "Done": "完成", + "Open": "打开", + "Closed": "关闭", + "LatestQuestionOnThe": "有关的最新问题", + "Stackoverflow": "Stackoverflow", + "Votes": "票数", + "Answer": "回答", + "Views": "观看次数", + "Answered": "已回答", + "WaitingForYourAnswer": "等待你的回答", + "Asked": "提问", + "AllQuestions": "所有的问题", + "NextVersion": "下一个版本", + "MilestoneErrorMessage": "无法从Github获取当前的里程碑详细信息.", + "QuestionItemErrorMessage": "无法从Stackoverflow获取最新的问题详细信息.", + "Oops": "哎呀!" + } + } + \ No newline at end of file diff --git a/docs/en/Background-Jobs-Quartz.md b/docs/en/Background-Jobs-Quartz.md index aa087ace0c..97327b5e8a 100644 --- a/docs/en/Background-Jobs-Quartz.md +++ b/docs/en/Background-Jobs-Quartz.md @@ -70,7 +70,43 @@ public class YourModule : AbpModule } ```` -Quartz stores job and scheduling information **in memory by default**. In the example, we use the pre-configuration of [options pattern](Options.md) to change it to the database. For more configuration of Quartz, please refer to the Quartz's [documentation](https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/index.html). +Starting from ABP 3.1 version, we have added `Configurator` to `AbpQuartzOptions` to configure Quartz. For example: + +````csharp +[DependsOn( + //...other dependencies + typeof(AbpBackgroundJobsQuartzModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + + PreConfigure(options => + { + options.Configurator = configure => + { + configure.UsePersistentStore(storeOptions => + { + storeOptions.UseProperties = true; + storeOptions.UseJsonSerializer(); + storeOptions.UseSqlServer(configuration.GetConnectionString("Quartz")); + storeOptions.UseClustering(c => + { + c.CheckinMisfireThreshold = TimeSpan.FromSeconds(20); + c.CheckinInterval = TimeSpan.FromSeconds(10); + }); + }); + }; + }); + } +} +```` + +> You can choose the way you favorite to configure Quaratz. + +Quartz stores job and scheduling information **in memory by default**. In the example, we use the pre-configuration of [options pattern](Options.md) to change it to the database. For more configuration of Quartz, please refer to the Quartz's [documentation](https://www.quartz-scheduler.net/). ## Exception handling diff --git a/docs/en/Blog-Posts/2020-08-20 v3_1_Release/Post.md b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/Post.md new file mode 100644 index 0000000000..b082ec171b --- /dev/null +++ b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/Post.md @@ -0,0 +1,247 @@ +# ABP Framework v3.1 RC.1 Has Been Released + +Today, we are releasing the **ABP Framework version 3.1 Release Candidate 1** (RC.1). The development cycle for this version was **~7 weeks**. It was the longest development cycle for a feature version release ever. We have completed **~150 issues**, merged **~150 PRs** and made **~1,000 commits** only in the main [abp repository](https://github.com/abpframework/abp). See the related [milestone](https://github.com/abpframework/abp/milestone/38?closed=1) on GitHub. + +There were two main reasons of this long development cycle; + +* We've switched to **4-weeks** release cycle (was discussed in [this issue](https://github.com/abpframework/abp/issues/4692)). +* We've [re-written](https://github.com/abpframework/abp/issues/4881) the Angular service proxy generation system using the Angular schematics to make it more stable. There were some problems with the previous implementation. + +This long development cycle brings a lot of new features, improvements and bug fixes. I will highlight the fundamental features and changes in this blog post. + +## About the Preview/Stable Version Cycle + +As mentioned above, it is planned to release a new stable feature version (like 3.1, 3.2, 3.3...) in every 4-weeks. + +In addition, we are starting to deploy a **preview version** 2-weeks before the stable versions for every feature/major releases. + +Today, we've released `3.1.0-rc.1` as the first preview version. We may release more previews if it is needed until the stable 3.1.0 version. + +**The stable `3.1.0` version will be released on September 3, 2020.** Next RC version, `3.2.0-rc.1`, is planned for September 17, 2020 (2 weeks after the stable 3.1.0 and 2 weeks before the stable 3.2.0). + +We **won't add new features** to a version after publishing the preview version. We only will make **bug fixes** until the stable version. The new features being developed in this period will be available in the next version. + +> We will use `-rc.x` suffix (like `3.1.0-rc.1` and `3.1.0-rc.2`) for preview releases. However, we may also publish with `-preview.x` suffix before RC (Release Candidate) releases, especially for major versions (like 4.x, 5.x...). + +### About the Nightly Builds + +Don't confuse preview versions vs nightly builds. When we say preview, we are mentioning the preview system explained above. + +We will continue to publish **nightly builds** for all the [ABP Framework packages](https://abp.io/packages). Nightly pages are built from the development branch. You can refer to [this document](https://docs.abp.io/en/abp/latest/Nightly-Builds) to learn how to use the nightly packages. + +## Get Started with the RC Versions + +Please try the preview versions and provide feedback to us to release more stable versions. Please open an issue on the [GitHub repository](https://github.com/abpframework/abp/issues/new) if you find a bug or want to give feedback. + +### Update the ABP CLI to the 3.1.0-rc.1 + +Since this is the first preview version, you need to upgrade the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) to the `3.1.0-rc.1` to be able to use the preview features: + +````bash +dotnet tool update Volo.Abp.Cli -g --version 3.1.0-rc.1 +```` + +### New Solutions + +The [ABP.IO](https://abp.io/) platform and the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) are compatible with the RC system. You can select the "preview" option on the [download page](https://abp.io/get-started) or use the "**--preview**" parameter with the ABP CLI [new](https://docs.abp.io/en/abp/latest/CLI?_ga=2.106435654.411298747.1597771169-1910388957.1594128976#new) command: + +````bash +abp new Acme.BookStore --preview +```` + +This command will create a new project with the latest RC/Preview version. Whenever the stable version is released, you can switch to the stable version for your solution using the `abp switch-to-stable` command in the root folder of your solution. + +### Existing Solutions + +If you already have a solution and want to use/test the latest RC/Preview version, use the `abp switch-to-preview` command in the root folder of your solution. You can return back to the latest stable using the `abp switch-to-stable ` command later. + +> Note that the `abp switch-to-preview` command was being used to switch to nightly builds before the v3.1. Now, you should use the `abp switch-to-nightly` for [nightly builds](https://docs.abp.io/en/abp/latest/Nightly-Builds). + +## What's New with the ABP Framework 3.1 RC.1 + +### Angular Service Proxies + +ABP provides a system to generate Angular service proxies (with TypeScript) to consume the HTTP APIs of your application. Service proxy generation system **has been completely re-written** with the ABP Framework 3.1. The main goal was to build more stable and feature rich system that is better aligned with other ABP Framework features (like [modularity](https://docs.abp.io/en/abp/latest/Module-Development-Basics)). + +[See the documentation](https://docs.abp.io/en/abp/latest/UI/Angular/Service-Proxies) to learn more about the service proxy generation for Angular applications. + +### Authorization Code Flow for the Angular UI + +We were using the **resource owner password authentication** flow for the Angular UI login page. We've implemented **Authorization Code Flow** for the Angular account module and made it **default for new projects**. With this change, the Angular application now redirects to the login page of the MVC UI which was implemented using the Identity Server 4. We also removed the client secret from the Angular side with this change. + +Old behavior remains exist. If you want to switch to the new flow (which is recommended), follow the steps below: + +1) Add `authorization_code` to the `IdentityServerClientGrantTypes` table in the database, for the client used by the Angular UI (the `ClientId` is `YourProjectName_App` by default, in the `IdentityServerClients` table). + +2) Add `http://localhost:4200` to `IdentityServerClientRedirectUris` and `IdentityServerClientPostLogoutRedirectUris` tables for the same client. + +3) Set `RequireClientSecret` to `false` in the `IdentityServerClients` table for the same client. + +> [ABP Commercial](https://commercial.abp.io/) users can make these changes on the [Identity Server Management UI](https://commercial.abp.io/modules/Volo.Identityserver.Ui). + +4) Change the `oAuthConfig` section in the `src/environments/environment.ts` file of the Angular application. + +You can take [this new configuration](https://gist.github.com/hikalkan/e7f6ae7f507b201783682dccaeadc5e3) as a reference. Main changes are; + +* Added `responseType` as `code`. +* Added `redirectUri` +* Added `offline_access` to the `scope`. +* Removed `oidc: false` option. +* Removed the client secret option. + +### Global Feature System + +The new "Global Features" system allows to **enable/disable features of an application or a module** in a central point. It is especially useful if you want to use a module but don't want to bring all its features into your application. If the module was so designed, you can enable only the features you need. + +When you disable a feature; + +* The **database tables** related to that feature should not be created in the database. +* The **HTTP APIs** related to that feature should not be exposed. They returns 404 if they are directly requested. + +So, the goal is that; when you disable a feature, it should behave like that feature doesn't exists in your system at all. + +There is **no way to enable/disable a global feature on runtime**. You should decide it in the development time (remember, even database tables are not being created for disabled global features, so you can't enable it on runtime). + +> "Global Features" system is different than [SaaS/multi-tenancy features](https://docs.abp.io/en/abp/latest/Features), where you can enable/disable features for your tenants on runtime. + +Assume that you are using the [CMS Kit module](https://github.com/abpframework/abp/tree/dev/modules/cms-kit) (this module is in a very early stage) where you only want to enable the comment feature: + +````csharp +GlobalFeatureManager.Instance.Modules.CmsKit().Comments.Enable(); +```` + +You can check if a feature was enabled: + +```csharp +GlobalFeatureManager.Instance.IsEnabled(); +``` + +Or you can add `[RequiresGlobalFeature(...)]` attribute to a controller/page to disable it if the related feature was disabled: + +```csharp +//... +[RequiresGlobalFeature(typeof(CommentsFeature))] +public class CommentController : AbpController +{ + //... +} +``` + +See the issue [#5061](https://github.com/abpframework/abp/issues/5061) until this is fully documented. + +### Social/External Logins + +Implemented the infrastructure for social/external logins in the account module. So, now you can easily configure your application to support social/external logins by [following the documentation](https://github.com/abpframework/abp/blob/dev/docs/en/Authentication/Social-External-Logins.md). Once you configure a provider, a button will appear on the login page to use this provider. + +The social logins will work as expected even if you are using the Angular UI, since the Angular UI uses the MVC login using the authorization code flow implemented with this new version (as explained above). + +### Forgot/Reset Password + +Implemented forgot password / password reset for the account module. + +You can now enter your email address to get an email containing a **password reset link**: + +![forgot-password](forgot-password.png) + +When you click to the link, you are redirected to a password reset page to determine your new password: + +![reset-password](reset-password.png) + +### External Login System + +The standard Social/External Login system (like Facebook login) works via OpenID Connect. That means the user is redirected to the login provider, logins there and redirected to your application. + +While this is pretty nice for most scenarios, sometimes you want a simpler external login mechanism: User enters username & password in your own application's login form and you check the username & password from another source, not from your own database. + +ABP v3.1 introduces an External Login System to check username & password from any source (from an external database, a REST service or from an LDAP / Active Directory server). + +You can check the [issue #4977](https://github.com/abpframework/abp/issues/4977#issuecomment-670006297) until it is fully documented. + +We've implemented LDAP authentication for the ABP Commercial, using this new login extension system (see the ABP Commercial section below). + +### User Security Logs + +The new [Security Log System](https://github.com/abpframework/abp/issues/4492) (of the Identity module) automatically logs all authentication related operations (login, logout, change password...) to a `AbpSecurityLogs` table in the database. + +### New BLOB Storage Providers + +Implemented [AWS](https://github.com/abpframework/abp/blob/dev/docs/en/Blob-Storing-Aws.md) and [Aliyun](https://github.com/abpframework/abp/blob/dev/docs/en/Blob-Storing-Aliyun.md) providers for the [BLOB storing](https://docs.abp.io/en/abp/latest/Blob-Storing) system with this version. + +### Other Features / Highlights + +Here, some other highlights from this release; + +* UOW level caching system [#4796](https://github.com/abpframework/abp/issues/4796) +* Refactored the console application template to better integrate to the host builder [#5006](https://github.com/abpframework/abp/issues/5006) +* [Volo.Abp.Ldap](https://www.nuget.org/packages/Volo.Abp.Ldap) package now supports multi-tenancy. +* Introduce `BasicAggregateRoot` base class [#4808](https://github.com/abpframework/abp/issues/4808) +* Sets GUID Id in the `InsertAsync` method of the EF Core repository if it was not set by the developer [#4634](https://github.com/abpframework/abp/pull/4634) +* Added `GetPagedListAsync` methods to the repository to simplify paging [#4617](https://github.com/abpframework/abp/pull/4617) +* Configured [Prettier](https://prettier.io/) for the startup template [#4318](https://github.com/abpframework/abp/issues/4318) +* Defined new layout hooks for the MVC UI: before page content & after page content [#4008](https://github.com/abpframework/abp/issues/4008) +* Allow to put static resources (js, css... files) under the Components folder for ASP.NET Core MVC UI. +* Upgraded to AutoMapper 10 and Quartz 3.1 for the related integration packages. + +## What's New with the ABP Commercial v3.1 RC.1 + +### Security Logs UI + +We've created a UI to report user security logs for authentication related operations, under the Identity Management menu: + +![security-logs-ui](security-logs-ui.png) + +Also, every user can see his/her own security logs by selecting the "My security logs" under the user menu: + +![my-security-logs](my-security-logs.png) + + + +### LDAP Authentication + +We've implemented LDAP authentication using the new external login system explained above. Also, created a UI to configure the server settings: + +![ldap-settings-ui](ldap-settings-ui.png) + +In this way, you can simply check passwords of the users from LDAP in the login page. If given username / password doesn't exists on LDAP, then it fallbacks to the local database, just like before. + +Since it supports **multi-tenancy**, you can enable, disable and configure it for your tenants. + +### Email / Phone Number Verification + +User profile management page now supports to Email & Phone Number verification flow: + +![email-phone-verification](email-phone-verification.png) + +When user clicks to the **verify** button, a verification email/SMS (that has a verification code) sent to the user and the UI waits to submit this code. + +### User Lock + +Implemented to **lock a user** for a given period of time. Locked users can not login to the application for the given period of time: + +![user-lock](user-lock.png) + +### ABP Suite: Angular UI Code Generation Revisited + +Angular UI code generation has been re-written using the Angular Schematics for the ABP Suite. It is now more stable and produces a better application code. + +ABP Suite also supports code generation on module development. + +### Others + +* **Social logins** and **authorization code flow** are also implemented for the ABP Commercial, just as described above. +* Added breadcrumb and file icons for the **file management module**. + +## The ABP Community + +We've lunched the [community.abp.io](https://community.abp.io/) ~two weeks ago with its initial version. It only has "Article submission" system for now. We are developing new exciting features. There will be an update in a few days and we'll publish a new blog post for it. + +## Conclusion + +The main goals of the 3.1 version were; + +* Complete the missing **authentication features** (like social logins, LDAP authentication, authorization code flow for the Angular UI...) for the ABP Framework & ABP Commercial. +* Re-write a stable and feature complete **Angular service proxy generation** system for the ABP Framework and CRUD UI generation system for the ABP Commercial. +* Develop a system to lunch **preview versions** of the platform. `3.1.0-rc.1` is the first preview version that has been published with this new system. +* Complete the fundamental **documentation & tutorials** (we've even created a [video tutorial series](https://www.youtube.com/watch?v=cJzyIFfAlp8&list=PLsNclT2aHJcPNaCf7Io3DbMN6yAk_DgWJ)). + +ABP.IO platform will be more mature & stable with the v3.1. Enjoy Coding! \ No newline at end of file diff --git a/docs/en/Blog-Posts/2020-08-20 v3_1_Release/email-phone-verification.png b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/email-phone-verification.png new file mode 100644 index 0000000000..226c93668d Binary files /dev/null and b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/email-phone-verification.png differ diff --git a/docs/en/Blog-Posts/2020-08-20 v3_1_Release/forgot-password.png b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/forgot-password.png new file mode 100644 index 0000000000..4bf2570a88 Binary files /dev/null and b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/forgot-password.png differ diff --git a/docs/en/Blog-Posts/2020-08-20 v3_1_Release/ldap-settings-ui.png b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/ldap-settings-ui.png new file mode 100644 index 0000000000..a88ca9fbcd Binary files /dev/null and b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/ldap-settings-ui.png differ diff --git a/docs/en/Blog-Posts/2020-08-20 v3_1_Release/my-security-logs.png b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/my-security-logs.png new file mode 100644 index 0000000000..fb5fa8a454 Binary files /dev/null and b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/my-security-logs.png differ diff --git a/docs/en/Blog-Posts/2020-08-20 v3_1_Release/reset-password.png b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/reset-password.png new file mode 100644 index 0000000000..5fefe36b01 Binary files /dev/null and b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/reset-password.png differ diff --git a/docs/en/Blog-Posts/2020-08-20 v3_1_Release/security-logs-ui.png b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/security-logs-ui.png new file mode 100644 index 0000000000..5f005b5c55 Binary files /dev/null and b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/security-logs-ui.png differ diff --git a/docs/en/Blog-Posts/2020-08-20 v3_1_Release/user-lock.png b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/user-lock.png new file mode 100644 index 0000000000..363c657376 Binary files /dev/null and b/docs/en/Blog-Posts/2020-08-20 v3_1_Release/user-lock.png differ diff --git a/docs/en/CLI.md b/docs/en/CLI.md index f7b5ab88aa..e420979efa 100644 --- a/docs/en/CLI.md +++ b/docs/en/CLI.md @@ -26,7 +26,8 @@ Here, the list of all available commands before explaining their details: * **`add-package`**: Adds an ABP package to a project. * **`add-module`**: Adds a [multi-package application module](https://docs.abp.io/en/abp/latest/Modules/Index) to a solution. * **`generate-proxy`**: Generates client side proxies to use HTTP API endpoints on the server. -* **`switch-to-preview`**: Switches to the latest [nightly builds](Nightly-Builds.md) of the ABP related packages on a solution. +* **`switch-to-preview`**: Switches to the latest preview version of the ABP Framework. +* **`switch-to-nightly`**: Switches to the latest [nightly builds](Nightly-Builds.md) of the ABP related packages on a solution. * **`switch-to-stable`**: Switches to the latest stable versions of the ABP related packages on a solution. * **`translate`**: Simplifies to translate localization files when you have multiple JSON [localization](Localization.md) files in a source control repository. * **`login`**: Authenticates on your computer with your [abp.io](https://abp.io/) username and password. @@ -79,8 +80,7 @@ abp new Acme.BookStore * `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side. * `none`: Without UI. There are some additional options for this template: * `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side. - * `--mobile` or `-m`: Specifies the mobile application framework. Default framework is `react-native`. Available frameworks: - * `none`: no mobile application. + * `--mobile` or `-m`: Specifies the mobile application framework. If not specified, no mobile application will be created. Available options: * `react-native`: React Native. * `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers: * `ef`: Entity Framework Core. @@ -90,7 +90,7 @@ abp new Acme.BookStore * **`console`**: [Console template](Startup-Templates/Console.md). * `--output-folder` or `-o`: Specifies the output folder. Default value is the current directory. * `--version` or `-v`: Specifies the ABP & template version. It can be a [release tag](https://github.com/abpframework/abp/releases) or a [branch name](https://github.com/abpframework/abp/branches). Uses the latest release if not specified. Most of the times, you will want to use the latest version. -* `--preview`: Use latest pre-release version (Only if `--version ` is not specified and there is at least one pre-release after latest stable version). +* `--preview`: Use latest preview version. * `--template-source` or `-ts`: Specifies a custom template source to use to build the project. Local and network sources can be used(Like `D:\local-template` or `https://.../my-template-file.zip`). * `--create-solution-folder` or `-csf`: Specifies if the project will be in a new folder in the output folder or directly the output folder. * `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True;MultipleActiveResultSets=true` for EF Core and it is configured to use the SQL Server. If you want to use the EF Core, but need to change the DBMS, you can change it as [described here](Entity-Framework-Core-Other-DBMS.md) (after creating the solution). @@ -111,7 +111,6 @@ abp update [options] #### Options -* `--include-previews` or `-p`: Includes preview, beta and rc packages while checking the latest versions. * `--npm`: Only updates NPM packages. * `--nuget`: Only updates NuGet packages. * `--solution-path` or `-sp`: Specify the solution path. Use the current directory by default @@ -197,7 +196,22 @@ abp generate-proxy --apiUrl https://localhost:44305 --ui angular --module all ### switch-to-preview -You can use this command to switch your project to latest **nightly** preview version of the ABP framework packages. +You can use this command to switch your project to latest preview version of the ABP framework. + +Usage: + +````bash +abp switch-to-preview [options] +```` + +#### Options + +`--solution-directory` or `-sd`: Specifies the directory. The solution should be in that directory or in any of its sub directories. If not specified, default is the current directory. + + +### switch-to-nightly + +You can use this command to switch your project to latest [nightly](Nightly-Builds.md) preview version of the ABP framework packages. Usage: @@ -211,7 +225,7 @@ abp switch-to-nightly [options] ### switch-to-stable -If you're using the ABP Framework preview packages, you can switch back to latest stable version using this command. +If you're using the ABP Framework preview packages (including nightly previews), you can switch back to latest stable version using this command. Usage: @@ -274,11 +288,13 @@ Then review changes on your source control system to be sure that it has changed Some features of the CLI requires to be logged in to abp.io platform. To login with your username write: ```bash -abp login # Asks password separately -abp login -p # Specify the password as a parameter +abp login # Allows you to enter your password hidden +abp login -p # Specify the password as a parameter (password is visible) +abp login --organization # If you have multiple organizations, you need set your active organization +abp login -p -o # You can enter both your password and organization in the same command ``` -> Using `-p` parameter might not be safe if someone is watching your screen :) It can be useful for automation purposes. +> When using the -p parameter, be careful as your password will be visible. It's useful for CI/CD automation pipelines. A new login with an already active session overwrites the previous session. diff --git a/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/Using-DevExtreme-In-ABP-Based-Application.md b/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/Using-DevExtreme-In-ABP-Based-Application.md new file mode 100644 index 0000000000..d319dbc70e --- /dev/null +++ b/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/Using-DevExtreme-In-ABP-Based-Application.md @@ -0,0 +1,270 @@ +## Using DevExtreme with ABP Based Applications + +Hi, in this step by step article, I will show you how to integrate DevExtreme components into ABP Framework based applications. + +## Install DevExtreme + +You can follow [this documentation](https://js.devexpress.com/Documentation/17_1/Guide/ASP.NET_MVC_Controls/Prerequisites_and_Installation/) to install devexpress packages. + +## Preparing the Project + +### Startup template and the initial run + +ABP Framework offers startup templates to get into the business faster. We can download a new startup template using [ABP CLI](https://docs.abp.io/en/abp/latest/CLI): + +````bash +abp new DevExtremeSample +```` + +After the download is finished, open the solution in the Visual Studio (or your favorite IDE): + +![initial-project](initial-project.png) + +Run the `DevExtremeSample.DbMigrator` application to create the database and seed initial data (which creates the admin user, admin role, related permissions, etc). Then we can run the `DevExtremeSample.Web` project to see our application working. + +> _Default admin username is **admin** and password is **1q2w3E\***_ + +### Adding DevExtreme NuGet Packages + +Add the `DevExtreme.AspNet.Core` NuGet package to the `DevExtremeSample.Application.Contracts` project. + +``` +Install-Package DevExtreme.AspNet.Core +``` + +Add the `DevExtreme.AspNet.Data` package to your `DevExtremeSample.Web` project. + +``` +Install-Package DevExtreme.AspNet.Data +``` + +> Please remember that, you must add _"DevExpress NuGet Feed"_ to your **Nuget Package Sources**. Check [this documentation](https://js.devexpress.com/Documentation/17_1/Guide/ASP.NET_MVC_Controls/Prerequisites_and_Installation/) for more information. + +### Adding DevExtreme NPM Depencies + +Open your `DevExtremeSample.Web` project folder with a command line and add `devextreme` NPM package: + +````bash +npm install devextreme +```` + +### Adding Resource Mappings + +The `devextreme` NPM package is saved under `node_modules` folder. We need to move the needed files in our `wwwroot/libs` folder to use them in our web project. We can do it using the ABP [client side resource mapping](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Client-Side-Package-Management) system. + +Open the `abp.resourcemapping.js` file in your `DevExtremeSample.Web` project and add the following definition to inside `mappings` object. + +````json +"@node_modules/devextreme/dist/**/*": "@libs/devextreme/" +```` + +The final `abp.resourcemapping.js` file should look like below: + +``` +module.exports = { + aliases: {}, + mappings: { + "@node_modules/devextreme/dist/**/*": "@libs/devextreme/", + }, +}; +``` + +Open your `DevExtremeSample.Web` project folder with a command line and run the `gulp` command. This command will copy the needed library files into the ``/wwwroot/libs/devextreme/` folder. + +![gulp](gulp.png) + +You can see `devextreme` folder inside the `wwwroot/libs`: + +![wwwroot-lib](wwwroot-lib.png) + +### Adding DevExtremeStyleContributor + +We will add DevExtreme CSS files to the global bundle by creating a [bundle contributor](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Bundling-Minification). + +Create a `Bundling` folder in the `DevExtremeSample.Web` project and a `DevExtremeStyleContributor.cs` file with the following content: + +```csharp +using System.Collections.Generic; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +namespace DevExtremeSample.Web.Bundling +{ + public class DevExtremeStyleContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.AddIfNotContains("/libs/devextreme/css/dx.common.css"); + context.Files.AddIfNotContains("/libs/devextreme/css/dx.light.css"); + } + } +} +``` + +> You can choose another theme than the light theme. Check the `/libs/devextreme/css/` folder and the DevExtreme documentation for other themes. + +Open your `DevExtremeSampleWebModule.cs` file in your `DevExtremeSample.Web` project and add following code into the `ConfigureServices` method: + +```csharp +Configure(options => +{ + options + .StyleBundles + .Get(StandardBundles.Styles.Global) + .AddContributors(typeof(DevExtremeStyleContributor)); +}); +``` + +### Adding DevExtremeScriptContributor + +We cannot add DevExtreme js packages to `Global Script Bundles`, because DevExtreme is using some inline javascript codes and js packages requires to located in the `` section. + +But we can create `ViewComponent` and render it at `` section with `AbpLayoutHookOptions`. + +First, create `DevExtremeScriptContributor.cs` file at your `Bundling` folder and copy following code block to inside it. + +```csharp +using System.Collections.Generic; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery; +using Volo.Abp.Modularity; + +namespace DevExtremeSample.Web.Bundling +{ + [DependsOn( + typeof(JQueryScriptContributor) + )] + public class DevExtremeScriptContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.AddIfNotContains("/libs/devextreme/js/dx.all.js"); + context.Files.AddIfNotContains("/libs/devextreme/js/dx.aspnet.mvc.js"); + context.Files.AddIfNotContains("/libs/devextreme/js/dx.aspnet.data.js"); + } + } +} +``` + +As you see, the `DevExtremeScriptContributor` is depends on `JQueryScriptContributor`. All packages are `JQueryScriptContributor` will added before the `DevExtremeScriptContributor` packages. + +#### Create DevExtremeJsViewComponent + +Create `Components` folder in your `DevExtremeSample.Web` project. Then create `DevExtremeJs` folder under `Components` folder. + +Create `Default.cshtml` file in your `DevExtremeJs` folder and paste following codes to your file. + +```csharp +@using DevExtremeSample.Web.Bundling +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling + + + + +``` + +Create `DevExtremeJsViewComponent.cs` file in your `DevExtremeJs` folder and paste following codes to your file. + +```csharp +namespace DevExtremeSample.Web.Components.DevExtremeJs +{ + public class DevExtremeJsViewComponent : AbpViewComponent + { + public IViewComponentResult Invoke() + { + return View("/Components/DevExtremeJs/Default.cshtml"); + } + } +} +``` + +After that, your `*.Web` project should be like as following. + +![devextreme-js](devextreme-js.png) + +Then we can add this view component to `` section by using **hooks**. + +Open your `DevExtremeSampleWebModule.cs` file in your `DevExtremeSample.Web` project and add following code to `ConfigureServices` method. + +```csharp +Configure(options => +{ + options.Add( + LayoutHooks.Head.Last, //The hook name + typeof(DevExtremeJsViewComponent) //The component to add + ); +}); +``` + +#### Known Issue: Uncaught TypeError: MutationObserver.observe: Argument 1 is not an object. + +> If you are using ABP 3.1 version or more, you can skip this section. + +When you run your `*.Web` project, you will see an exception (`Uncaught TypeError: MutationObserver.observe: Argument 1 is not an object.`) at your console. + +This article is written in ABP Version 3.1 development cycle. We have fixed this issue [(ref)](https://github.com/abpframework/abp/issues/4566) in this development cycle. + +To fix that issue, download this file [abp.jquery.js](https://github.com/abpframework/abp/blob/dev/npm/packs/jquery/src/abp.jquery.js) and replace with `wwwroot / libs / abp / jquery / abp.jquery.js` file of your `*.Web` project. + +### Result + +After following this step-by-step article you can use all DevExtreme features in your project. + +![devexp-result](devexp-result.gif) + +> The result example is created by following [this documentation](https://js.devexpress.com/Demos/WidgetsGallery/Demo/ProgressBar/Overview/NetCore/Light/). + +--- + +### Sample Application + +We have created a sample application with [Tree List](https://demos.devexpress.com/ASPNetCore/Demo/TreeList/Overview/) and [Data Grid](https://demos.devexpress.com/ASPNetCore/Demo/DataGrid/Overview/) examples. + +You can download the source code from [here](https://github.com/abpframework/abp-samples/tree/master/DevExtreme-Mvc). + +We have some notes about this sample and general usages of DevExtreme at ABP based application. + +### Data Storage + +We will use an in-memory list for using data storage for this sample. + +There is a `SampleDataService.cs` file in `Data` folder at `*.Application.Contracts` project. We store all sample data here. + +We did not create `Entities` etc. Because we want to show "How to use DevExtreme?", because of that, in this sample we focused to application and UI layer. + +### JSON Serialization + +You can see some `[JsonProperty(Name = "OrderId")]` attributes at DTO's. In this sample, we use that attribute on DTO's properties because DevExtreme official resource is suggesting to _disable the conversion in the JSON serializer_ [(ref)](https://js.devexpress.com/Documentation/19_1/Guide/Angular_Components/Visual_Studio_Integration/Add_DevExtreme_to_an_ASP.NET_Core_Angular_Application/#Troubleshooting). **DO NOT DO THAT!** + +If you change **the conversion in the JSON serializer**, some pre-build abp modules may occur a problem. + +### MVC + +You can use some DevExtreme functions to create UI. The following code blocks show you how you can use it with ABP Applicaion Services. + +```csharp +Html.DevExtreme().DataGrid() + .DataSource(d => d.Mvc() + .Controller("Order") // Application Service Name 'without **AppService**' + .LoadAction("GetOrders") // Method Name 'without **Async**' + .InsertAction("InsertOrder") + .UpdateAction("UpdateOrder") + .DeleteAction("DeleteOrder") + .Key("OrderID") + ) +``` + +```csharp +public class OrderAppService : DevExtremeSampleAppService, IOrderAppService +{ + public async Task GetOrdersAsync(DataSourceLoadOptions loadOptions) + { + ... + } + + public async Task InsertOrder(string values) + { + ... + } + ... +} +``` diff --git a/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/devexp-result.gif b/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/devexp-result.gif new file mode 100644 index 0000000000..03b0875468 Binary files /dev/null and b/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/devexp-result.gif differ diff --git a/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/devextreme-js.png b/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/devextreme-js.png new file mode 100644 index 0000000000..9252e74dbc Binary files /dev/null and b/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/devextreme-js.png differ diff --git a/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/gulp.png b/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/gulp.png new file mode 100644 index 0000000000..4c84e5410d Binary files /dev/null and b/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/gulp.png differ diff --git a/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/initial-project.png b/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/initial-project.png new file mode 100644 index 0000000000..fa2516d23d Binary files /dev/null and b/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/initial-project.png differ diff --git a/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/wwwroot-lib.png b/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/wwwroot-lib.png new file mode 100644 index 0000000000..26f9a6be72 Binary files /dev/null and b/docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/wwwroot-lib.png differ diff --git a/docs/en/Customizing-Application-Modules-Extending-Entities.md b/docs/en/Customizing-Application-Modules-Extending-Entities.md index a7c3f4e7cd..bafcc01da2 100644 --- a/docs/en/Customizing-Application-Modules-Extending-Entities.md +++ b/docs/en/Customizing-Application-Modules-Extending-Entities.md @@ -43,7 +43,10 @@ Assume that you want to add a `SocialSecurityNumber` to the `IdentityUser` entit ObjectExtensionManager.Instance .MapEfCoreProperty( "SocialSecurityNumber", - b => { b.HasMaxLength(32); } + (entityBuilder, propertyBuilder) => + { + propertyBuilder.HasMaxLength(32); + } ); ```` diff --git a/docs/en/Emailing.md b/docs/en/Emailing.md index 75069aacbe..aa341a3898 100644 --- a/docs/en/Emailing.md +++ b/docs/en/Emailing.md @@ -1,3 +1,250 @@ -# Emailing +# Email Sending -TODO! \ No newline at end of file +ABP Framework provides various services, settings and integrations for sending emails; + +* Provides `IEmailSender` service that is used to send emails. +* Defines [settings](Settings.md) to configure email sending. +* Integrates to the [background job system](Background-Jobs.md) to send emails via background jobs. +* Provides [MailKit integration](MailKit.md) package. + +## Installation + +> This package is already installed if you are using the [application startup template](Startup-Templates/Application.md). + +It is suggested to use the [ABP CLI](CLI.md) to install this package. Open a command line window in the folder of the project (.csproj file) and type the following command: + +````bash +abp add-package Volo.Abp.Emailing +```` + +If you haven't done it yet, you first need to install the ABP CLI. For other installation options, see [the package description page](https://abp.io/package-detail/Volo.Abp.Emailing). + +## Sending Emails + +### IEmailSender + +[Inject](Dependency-Injection.md) the `IEmailSender` into any service and use the `SendAsync` method to send emails. + +**Example** + +````csharp +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Emailing; + +namespace MyProject +{ + public class MyService : ITransientDependency + { + private readonly IEmailSender _emailSender; + + public MyService(IEmailSender emailSender) + { + _emailSender = emailSender; + } + + public async Task DoItAsync() + { + await _emailSender.SendAsync( + "target@domain.com", // target email address + "Email subject", // subject + "This is email body..." // email body + ); + } + } +} +```` + +`SendAsync` method has overloads to supply more parameters like; + +* **from**: You can set this as the first argument to set a sender email address. If not provided, the default sender address is used (see the email settings below). +* **isBodyHtml**: Indicates whether the email body may contain HTML tags. **Default: true**. + +> `IEmailSender` is the suggested way to send emails, since it makes your code provider independent. + +#### MailMessage + +In addition to primitive parameters, you can pass a **standard `MailMessage` object** ([see](https://docs.microsoft.com/en-us/dotnet/api/system.net.mail.mailmessage)) to the `SendAsync` method to set more options, like adding attachments. + +### ISmtpEmailSender + +Sending emails is implemented by the standard `SmtpClient` class ([see](https://docs.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient)) by default. The implementation class is the `SmtpEmailSender`. This class also expose the `ISmtpEmailSender` service (in addition to the `IEmailSender`). + +Most of the time you want to directly use the `IEmailSender` to make your code provider independent. However, if you want to create an `SmtpClient` object with the same email settings, you can inject the `ISmtpEmailSender` and use its `BuildClientAsync` method to obtain a `SmtpClient` object and send the email yourself. + +## Queueing Emails / Background Jobs + +`IEmailSender` has a `QueueAsync` method that can be used to add emails to the background job queue to send them in a background thread. In this way, you don't take time of the user by waiting to send the email. `QueueAsync` method gets the same arguments with the `SendAsync` method. + +Queueing emails tolerates errors since the background job system has re-try mechanism to overcome temporary network/server problems. + +See the [background jobs document](Background-Jobs.md) for more about the background job system. + +## Email Settings + +Email sending uses the [setting system](Settings.md) to define settings and get the values of these settings on the runtime. `Volo.Abp.Emailing.EmailSettingNames` defines constants for the setting names, just listed below: + +* **Abp.Mailing.DefaultFromAddress**: Used as the sender's email address when you don't specify a sender when sending emails (just like in the example above). +* **Abp.Mailing.DefaultFromDisplayName**: Used as the sender's display name when you don't specify a sender when sending emails (just like in the example above). +* **Abp.Mailing.Smtp.Host**: The IP/Domain of the SMTP server (default: 127.0.0.1). +* **Abp.Mailing.Smtp.Port**: The Port of the SMTP server (default: 25). +* **Abp.Mailing.Smtp.UserName**: Username, if the SMTP server requires authentication. +* **Abp.Mailing.Smtp.Password**: Password, if the SMTP server requires authentication. **This value is encrypted **(see the section below). +* **Abp.Mailing.Smtp.Domain**: Domain for the username, if the SMTP server requires authentication. +* **Abp.Mailing.Smtp.EnableSsl**: A value that indicates if the SMTP server uses SSL or not ("true" or "false". Default: "false"). +* **Abp.Mailing.Smtp.UseDefaultCredentials**: If true, uses default credentials instead of the provided username and password ("true" or "false". Default: "true"). + +The easiest way to define these settings it to add them to the `appsettings.json` file. The [application startup template](Startup-Templates/Application.md) already has these settings in the `appsettings.json`: + +````json +"Settings": { + "Abp.Mailing.Smtp.Host": "127.0.0.1", + "Abp.Mailing.Smtp.Port": "25", + "Abp.Mailing.Smtp.UserName": "", + "Abp.Mailing.Smtp.Password": "", + "Abp.Mailing.Smtp.Domain": "", + "Abp.Mailing.Smtp.EnableSsl": "false", + "Abp.Mailing.Smtp.UseDefaultCredentials": "true", + "Abp.Mailing.DefaultFromAddress": "noreply@abp.io", + "Abp.Mailing.DefaultFromDisplayName": "ABP application" +} +```` + +You can set/change these settings using the `ISettingManager` and store values in a database. See the [setting system document](Settings.md) to understand the setting system better. + +### Encrypt the SMTP Password + +*Abp.Mailing.Smtp.Password* must be an **encrypted** value. If you use the `ISettingManager` to set the password, you don't have to worry. It internally encrypts the values on set and decrypts on get. + +If you use the `appsettings.json` to store the password, you should manually inject the `ISettingEncryptionService` and use its `Encrypt` method to obtain an encrypted value. This can be done by creating a simple code in your application. Then you can delete the code. As better, you can create a UI in your application to configure the email settings. In this case, you can directly use the `ISettingManager` without worrying the encryption. + +### ISmtpEmailSenderConfiguration + +If you don't want to use the setting system to store the email sending configuration, you can replace the `ISmtpEmailSenderConfiguration` service with your own implementation to get the configuration from any other source. `ISmtpEmailSenderConfiguration` is implemented by the `SmtpEmailSenderConfiguration` by default, which gets the configuration from the setting system as explained above. + +## Text Template Integration + +ABP Framework provides a strong and flexible [text templating system](Text-Templating.md). You can use the text templating system to create dynamic email contents. Inject the `ITemplateRenderer` and use the `RenderAsync` to render a template. Then use the result as the email body. + +While you can define and use your own text templates, email sending system provides two simple built-in text templates. + +**Example: Use the standard and simple message template to send emails** + +````csharp +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Emailing; +using Volo.Abp.Emailing.Templates; +using Volo.Abp.TextTemplating; + +namespace Acme.BookStore.Web +{ + public class MyService : ITransientDependency + { + private readonly IEmailSender _emailSender; + private readonly ITemplateRenderer _templateRenderer; + + public MyService( + IEmailSender emailSender, + ITemplateRenderer templateRenderer) + { + _emailSender = emailSender; + _templateRenderer = templateRenderer; + } + + public async Task DoItAsync() + { + var body = await _templateRenderer.RenderAsync( + StandardEmailTemplates.Message, + new + { + message = "This is email body..." + } + ); + + await _emailSender.SendAsync( + "target-address@domain.com", + "Email subject", + body + ); + } + } +} +```` + +The resulting email body will be shown below: + +````html + + + + + + + This is email body... + + +```` + +Emailing system defines the built-in text templates with the given names: + +"**Abp.StandardEmailTemplates.Message**" is simplest template that has a text message: + +````html +{%{{{model.message}}}%} +```` + +This template uses the "Abp.StandardEmailTemplates.Layout" as its layout. + +"**Abp.StandardEmailTemplates.Layout**" is a simple template to provide an HTML document layout: + +````html + + + + + + + {%{{{content}}}%} + + +```` + +The final rendered message was shown above. + +> These template names are contants defined in the `Volo.Abp.Emailing.Templates.StandardEmailTemplates` class. + +### Overriding/Replacing the Standard Templates + +You typically want to replace the standard templates with your own ones, so you can prepare a branded email messages. To do that, you can use the power of the [virtual file system](Virtual-File-System.md) (VFS) or replace them in your own template definition provider. + +Pathes of the templates in the virtual file system are shown below: + +* `/Volo/Abp/Emailing/Templates/Layout.tpl` +* `/Volo/Abp/Emailing/Templates/Message.tpl` + +If you add files to the same localization in the virtual file system, your files will override them. + +Templates are inline localized, that means you can take the power of the [localization system](Localization.md) to make your templates multi-cultural. + +See the [text templating system](Text-Templating.md) document for details. + +> Notice that you can define and use your own templates for your application, rather than using the standard simple templates. These standard templates are mostly for reusable modules where they don't define their own templates but rely on the built-in ones. This makes easy to customize emails sent by the used modules, by just overriding the standard email layout template. + +## NullEmailSender + +`NullEmailSender` is a built-in class that implements the `IEmailSender`, but writes email contents to the [standard log system](Logging.md), rathen than actually sending the emails. + +This class can be useful especially in development time where you generally don't want to send real emails. The [application startup template](Startup-Templates/Application.md) already uses this class in the **DEBUG mode** with the following configuration in the domain layer: + +````csharp +#if DEBUG + context.Services.Replace(ServiceDescriptor.Singleton()); +#endif +```` + +So, don't confuse if you don't receive emails on DEBUG mode. Emails will be sent as expected on production (RELEASE mode). Remove these lines if you want to send real emails on DEBUG too. + +## See Also + +* [MailKit integration for sending emails](MailKit.md) \ No newline at end of file diff --git a/docs/en/Entity-Framework-Core-Migrations.md b/docs/en/Entity-Framework-Core-Migrations.md index d59f9f96c7..a93ee217da 100644 --- a/docs/en/Entity-Framework-Core-Migrations.md +++ b/docs/en/Entity-Framework-Core-Migrations.md @@ -398,7 +398,7 @@ builder.Entity(b => You've configured the custom property for your `DbContext` that is used by your application on the runtime. We also need to configure the `MigrationsDbContext`. -Instead of directly changing the `MigrationsDbContext`, we should use the entity extension system of the ABP Framework. Find the `YourProjectNameEntityExtensions` class in the `.EntityFrameworkCore` project of your solution (`BookStoreEntityExtensions` for this example) and change it as shown below: +Instead of directly changing the `MigrationsDbContext`, we **should** use the entity extension system of the ABP Framework. Find the `YourProjectNameEfCoreEntityExtensionMappings` class in the `.EntityFrameworkCore` project of your solution (`BookStoreEfCoreEntityExtensionMappings` for this example) and change it as shown below: ````csharp public static class MyProjectNameEntityExtensions @@ -412,7 +412,10 @@ public static class MyProjectNameEntityExtensions ObjectExtensionManager.Instance .MapEfCoreProperty( "Title", - builder => { builder.HasMaxLength(64); } + (entityBuilder, propertyBuilder) => + { + propertyBuilder.HasMaxLength(128); + } ); }); } diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index 66f9d3c2f9..98163e7c28 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -325,13 +325,16 @@ This section only explains the EF Core related usage of the `ObjectExtensionMana ObjectExtensionManager.Instance .MapEfCoreProperty( "Title", - builder => { builder.HasMaxLength(64); } + (entityBuilder, propertyBuilder) => + { + propertyBuilder.HasMaxLength(64); + } ); ```` If the related module has implemented this feature (by using the `ConfigureEfCoreEntity` explained below), then the new property is added to the model. Then you need to run the standard `Add-Migration` and `Update-Database` commands to update your database to add the new field. ->`MapEfCoreProperty` method must be called before using the related `DbContext`. It is a static method. The best way is to use it in your application as earlier as possible. The application startup template has a `YourProjectNameEntityExtensions` class that is safe to use this method inside. +>`MapEfCoreProperty` method must be called before using the related `DbContext`. It is a static method. The best way is to use it in your application as earlier as possible. The application startup template has a `YourProjectNameEfCoreEntityExtensionMappings` class that is safe to use this method inside. ### ConfigureEfCoreEntity diff --git a/docs/en/Exception-Handling.md b/docs/en/Exception-Handling.md index 3ccade9767..59a2c6bd0e 100644 --- a/docs/en/Exception-Handling.md +++ b/docs/en/Exception-Handling.md @@ -320,3 +320,14 @@ Some exception types are automatically thrown by the framework: - `EntityNotFoundException` is thrown if the requested entity is not available. This is mostly thrown by [repositories](Repositories.md). You can also throw these type of exceptions in your code (although it's rarely needed). + +## Send exception details to the client + +You can send exceptions to the client via the `SendExceptionsDetailsToClients` property of the `AbpExceptionHandlingOptions` class: + +````csharp +services.Configure(options => +{ + options.SendExceptionsDetailsToClients = true; +}); +```` diff --git a/docs/en/Getting-Started-React-Native.md b/docs/en/Getting-Started-React-Native.md index d591c93956..ec27d09463 100644 --- a/docs/en/Getting-Started-React-Native.md +++ b/docs/en/Getting-Started-React-Native.md @@ -2,7 +2,7 @@ ABP platform provide basic [React Native](https://reactnative.dev/) startup template to develop mobile applications **integrated to your ABP based backends**. -When you **create a new application** as described in the [getting started document](Getting-Started.md), the solution includes the React Native application in the `react-native` folder as default. +When you **create a new application** as described in the [getting started document](Getting-Started.md), you have to add `-m react-native` option to include `react-native` project in your solution. ## Configure Your Local IP Address diff --git a/docs/en/Getting-Started.md b/docs/en/Getting-Started.md index 57981d3233..55fcafe132 100644 --- a/docs/en/Getting-Started.md +++ b/docs/en/Getting-Started.md @@ -65,8 +65,6 @@ Use the `new` command of the ABP CLI to create a new project: abp new Acme.BookStore{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}} ```` -> This command also creates a React Native mobile application inside the solution folder. If you don't want it, you can safely delete it or specify the `-m none` option to the `abp new` command to not include it in the solution at all. - {{ if UI == "NG" }} * `-u` argument specifies the UI framework, `angular` in this case. @@ -126,7 +124,6 @@ There are three folders in the created solution: * `angular` folder contains the Angular UI application. * `aspnet-core` folder contains the backend solution. -* `react-native` folder contains the React Native UI application. Open the `.sln` (Visual Studio solution) file under the `aspnet-core` folder: @@ -339,11 +336,7 @@ Enter **admin** as the username and **1q2w3E*** as the password to login to the ## Mobile Development -When you create a new application, the solution includes `react-native` folder by default. This is a basic [React Native](https://reactnative.dev/) startup template to develop mobile applications integrated to your ABP based backends. - -If you don't plan to develop a mobile application with React Native, you can safely delete the `react-native` folder. - -> You can specifying the `-m none` option to the ABP CLI to not create the `react-native` folder in the beginning. +If you want to include a [React Native](https://reactnative.dev/) project in your solution, add `-m react-native` (or `--mobile react-native`) argument to project creation command. This is a basic React Native startup template to develop mobile applications integrated to your ABP based backends. See the "[Getting Started with the React Native](Getting-Started-React-Native.md)" document to learn how to configure and run the React Native application. diff --git a/docs/en/Global-Features.md b/docs/en/Global-Features.md new file mode 100644 index 0000000000..a0a593288c --- /dev/null +++ b/docs/en/Global-Features.md @@ -0,0 +1,3 @@ +# Global Features + +TODO (see [#5061](https://github.com/abpframework/abp/issues/5061) until this is documented). \ No newline at end of file diff --git a/docs/en/MailKit.md b/docs/en/MailKit.md new file mode 100644 index 0000000000..de4b653f4d --- /dev/null +++ b/docs/en/MailKit.md @@ -0,0 +1,48 @@ +# MailKit Integration + +[MailKit](http://www.mimekit.net/) is a cross-platform, popular open source mail client library for .net. ABP Framework provides an integration package to use the MailKit as the [email sender](Emailing.md). + +## Installation + +It is suggested to use the [ABP CLI](CLI.md) to install this package. Open a command line window in the folder of the project (.csproj file) and type the following command: + +````bash +abp add-package Volo.Abp.MailKit +```` + +If you haven't done it yet, you first need to install the ABP CLI. For other installation options, see [the package description page](https://abp.io/package-detail/Volo.Abp.MailKit). + +## Sending Emails + +### IEmailSender + +[Inject](Dependency-Injection.md) the standard `IEmailSender` into any service and use the `SendAsync` method to send emails. See the [email sending document](Emailing.md) for details. + +> `IEmailSender` is the suggested way to send emails even if you use MailKit, since it makes your code provider independent. + +### IMailKitSmtpEmailSender + +MailKit package also exposes the `IMailKitSmtpEmailSender` service that extends the `IEmailSender` by adding the `BuildClientAsync()` method. This method can be used to obtain a `MailKit.Net.Smtp.SmtpClient` object that can be used to perform MailKit specific operations. + +## Configuration + +MailKit integration package uses the same settings defined by the email sending system. So, refer to the [email sending document](Emailing.md) for the settings. + +In addition to the standard settings, this package defines `AbpMailKitOptions` as a simple [options](Options.md) class. This class defines only one options: + +* **SecureSocketOption**: Used to set one of the `SecureSocketOptions`. Default: `null` (uses the defaults). + +**Example: Use *SecureSocketOptions.SslOnConnect*** + +````csharp +Configure(options => +{ + options.SecureSocketOption = SecureSocketOptions.SslOnConnect; +}); +```` + +Refer to the [MailKit documentation](http://www.mimekit.net/) to learn more about this option. + +## See Also + +* [Email sending](Emailing.md) \ No newline at end of file diff --git a/docs/en/Module-Development-Basics.md b/docs/en/Module-Development-Basics.md index 6aac90c294..e6f80233e4 100644 --- a/docs/en/Module-Development-Basics.md +++ b/docs/en/Module-Development-Basics.md @@ -1,4 +1,4 @@ -# Module Development +# Modularity ## Introduction diff --git a/docs/en/Module-Entity-Extensions.md b/docs/en/Module-Entity-Extensions.md new file mode 100644 index 0000000000..61a7093dc5 --- /dev/null +++ b/docs/en/Module-Entity-Extensions.md @@ -0,0 +1,3 @@ +# Module Entity Extensions + +See https://docs.abp.io/en/commercial/latest/guides/module-entity-extensions (it will be moved here soon). \ No newline at end of file diff --git a/docs/en/Nightly-Builds.md b/docs/en/Nightly-Builds.md index 315f5109b9..f835dcda9e 100644 --- a/docs/en/Nightly-Builds.md +++ b/docs/en/Nightly-Builds.md @@ -2,40 +2,18 @@ All framework & module packages are deployed to MyGet every night in weekdays. So, you can use or test the latest code without waiting the next release. -## Configure Visual Studio +## Install & Uninstall Nightly Preview Packages -> Requires Visual Studio 2017+ - -1. Go to `Tools > Options > NuGet Package Manager > Package Source`. -2. Click the green `+` icon. -3. Set `ABP Nightly` as *Name* and `https://www.myget.org/F/abp-nightly/api/v3/index.json` as the *Source* as shown below: - ![night-build-add-nuget-source](images/night-build-add-nuget-source.png) -4. Click the `Update` button. -5. Click the `OK` button to save changes. - -## Install Package - -Now, you can install preview / nightly packages to your project from Nuget Browser or Package Manager Console. - -![night-build-add-nuget-package](images/night-build-add-nuget-package.png) - -1. In the nuget browser, select "Include prereleases". -2. Change package source to "All". -3. Search a package. You will see prereleases of the package formatted as `(VERSION)-preview(DATE)` (like *v0.16.0-preview20190401* in this sample). -4. You can click to the `Install` button to add package to your project. - -## Install & Uninstall Preview NPM Packages - -The latest version of preview NPM packages can be installed by the running below command in the root folder of application: +The latest version of nightly preview packages can be installed by the running below command in the root folder of application: ```bash -abp switch-to-preview --npm +abp switch-to-nightly ``` -If you're using the ABP Framework preview packages, you can switch back to stable version using this command: +If you're using the ABP Framework nightly preview packages, you can switch back to stable version using this command: ```bash -abp switch-to-stable --npm +abp switch-to-stable ``` See the [ABP CLI documentation](./CLI.md) for more information. diff --git a/docs/en/Previews.md b/docs/en/Previews.md new file mode 100644 index 0000000000..5d52a4ad69 --- /dev/null +++ b/docs/en/Previews.md @@ -0,0 +1,40 @@ +# Preview Releases + +The preview versions are released **~2 weeks before** releasing a major or feature version of the ABP Framework. They are released for developers to try and provide feedback to have more stable versions. + +Versioning of a preview release is like that: + +* 3.1.0-rc.1 +* 4.0.0-rc.1 + +More than one preview releases (like 3.1.0-rc.2 and 3.1.0-rc.3) might be published until the stable version (like 3.1.0). + +## Using the Preview Versions + +### New Solutions + +To create a project for testing the preview version, you can select the "**preview**" option on the [download page](https://abp.io/get-started) or use the "**--preview**" parameter with the [ABP CLI](CLI.md) new command: + +````bash +abp new Acme.BookStore --preview +```` + +This command will create a new project using the latest preview NuGet packages, NPM packages and the solution template. Whenever the stable version is released, you can switch to the stable version for your solution using the `abp switch-to-stable` command in the root folder of your solution. + +### Existing Solutions + +If you already have a solution and want to use/test the latest preview version, use the following [ABP CLI](CLI.md) command in the root folder of your solution. + +````bash +abp switch-to-preview +```` + +You can return back to the latest stable using the `abp switch-to-stable ` command later. + +````bash +abp switch-to-stable +```` + +## Providing Feedback + +You can open an issue on the [GitHub repository](https://github.com/abpframework/abp/issues/new), if you find a bug or want to provide any kind of feedback. \ No newline at end of file diff --git a/docs/en/Repositories.md b/docs/en/Repositories.md index 5412bbe187..a222b9d803 100644 --- a/docs/en/Repositories.md +++ b/docs/en/Repositories.md @@ -6,7 +6,11 @@ Repositories, in practice, are used to perform database operations for domain ob ## Generic Repositories -ABP can provide a **default generic repository** for each aggregate root or entity. You can [inject](Dependency-Injection.md) `IRepository` into your service and perform standard **CRUD** operations. Example usage: +ABP can provide a **default generic repository** for each aggregate root or entity. You can [inject](Dependency-Injection.md) `IRepository` into your service and perform standard **CRUD** operations. + +> Database provider layer should be properly configured to be able to use the default generic repositories. It is **already done** if you've created your project using the startup templates. If not, refer to the database provider documents ([EF Core](Entity-Framework-Core.md) / [MongoDB](MongoDB.md)) to configure it. + +**Example usage of a default generic repository:** ````C# public class PersonAppService : ApplicationService diff --git a/docs/en/Startup-Templates/Application.md b/docs/en/Startup-Templates/Application.md index 7495b8f006..4e9bd63371 100644 --- a/docs/en/Startup-Templates/Application.md +++ b/docs/en/Startup-Templates/Application.md @@ -54,6 +54,20 @@ Use `-d` (or `--database-provider`) option to specify the database provider: abp new Acme.BookStore -d mongodb ```` +### Specify the Mobile Application Framework + +This template supports the following mobile application frameworks: + +- `react-native`: React Native + +Use `-m` (or `--mobile`) option to specify the mobile application framework: + +````bash +abp new Acme.BookStore -m react-native +```` + +If not specified, no mobile application will be created. + ## Solution Structure Based on the options you've specified, you will get a slightly different solution structure. @@ -261,11 +275,10 @@ You should run the application with the given order: ### Angular UI -If you choose `Angular` as the UI framework (using the `-u angular` option), the solution is being separated into three folders: +If you choose `Angular` as the UI framework (using the `-u angular` option), the solution is being separated into two folders: * `angular` folder contains the Angular UI application, the client-side code. * `aspnet-core` folder contains the ASP.NET Core solution, the server-side code. -* `react-native` folder contains the React Native UI application, the client-side code for mobile. The server-side is similar to the solution described above. `*.HttpApi.Host` project serves the API, so the `Angular` application consumes it. @@ -356,7 +369,7 @@ See the [testing document](https://angular.io/guide/testing). ### React Native -The solution includes the [React Native](https://reactnative.dev/) application in the `react-native` folder as default. +if `-m react-native` option is spesified in new project command, the solution includes the [React Native](https://reactnative.dev/) application in the `react-native` folder. The server-side is similar to the solution described above. `*.HttpApi.Host` project serves the API, so the React Native application consumes it. diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index 3008db5c41..07f3b141ab 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -380,6 +380,112 @@ The rendering result will be: A global object value: TEST VALUE ```` +## Replacing the Existing Templates + +It is possible to replace a template defined by a module that used in your application. In this way, you can customize the templates based on your requirements without changing the module code. + +### Option-1: Using the Virtual File System + +The [Virtual File System](Virtual-File-System.md) allows you to override any file by placing the same file into the same path in your project. + +#### Example: Replace the Standard Email Layout Template + +ABP Framework provides an [email sending system](Emailing.md) that internally uses the text templating to render the email content. It defines a standard email layout template in the `/Volo/Abp/Emailing/Templates/Layout.tpl` path. The unique name of the template is `Abp.StandardEmailTemplates.Layout` and this string is defined as a constant on the `Volo.Abp.Emailing.Templates.StandardEmailTemplates` static class. + +Do the following steps to replace the template file with your own; + +**1)** Add a new file into the same location (`/Volo/Abp/Emailing/Templates/Layout.tpl`) in your project: + +![replace-email-layout](images/replace-email-layout.png) + +**2)** Prepare your email layout template: + +````html + + + + + + +

This my header

+ + {%{{{content}}}%} + +
+ This is my footer... +
+ + +```` + +This example simply adds a header and footer to the template and renders the content between them (see the *Layout Templates* section above to understand it). + +**3)** Configure the embedded resources in the `.csproj` file + +* Add [Microsoft.Extensions.FileProviders.Embedded](https://www.nuget.org/packages/Microsoft.Extensions.FileProviders.Embedded) NuGet package to the project. +* Add `true` into the `...` section of your `.csproj` file. +* Add the following code into your `.csproj` file: + +````xml + + + + +```` + +This makes the template files "embedded resource". + +**4)** Configure the virtual file system + +Configure the `AbpVirtualFileSystemOptions` in the `ConfigureServices` method of your [module](Module-Development-Basics.md) to add the embedded files into the virtual file system: + +```csharp +Configure(options => +{ + options.FileSets.AddEmbedded(); +}); +``` + +`BookStoreDomainModule` should be your module name, in this example code. + +> Be sure that your module (directly or indirectly) [depends on](Module-Development-Basics.md) the `AbpEmailingModule`. Because the VFS can override files based on the dependency order. + +Now, your template will be used when you want to render the email layout template. + +### Option-2: Using the Template Definition Provider + +You can create a template definition provider class that gets the email layout template and changes the virtual file path for the template. + +**Example: Use the `/MyTemplates/EmailLayout.tpl` file instead of the standard template** + +```csharp +using Volo.Abp.DependencyInjection; +using Volo.Abp.Emailing.Templates; +using Volo.Abp.TextTemplating; + +namespace MyProject +{ + public class MyTemplateDefinitionProvider + : TemplateDefinitionProvider, ITransientDependency + { + public override void Define(ITemplateDefinitionContext context) + { + var emailLayoutTemplate = context.GetOrNull(StandardEmailTemplates.Layout); + + emailLayoutTemplate + .WithVirtualFilePath( + "/MyTemplates/EmailLayout.tpl", + isInlineLocalized: true + ); + } + } +} +``` + +You should still add the file `/MyTemplates/EmailLayout.tpl` to the virtual file system as explained before. This approach allows you to locate templates in any folder instead of the folder defined by the depended module. + +Beside the template content, you can manipulate the template definition properties, like `DisplayName`, `Layout` or `LocalizationSource`. + ## Advanced Features This section covers some internals and more advanced usages of the text templating system. diff --git a/docs/en/Timing.md b/docs/en/Timing.md index 4d48fa6d51..afa4abda71 100644 --- a/docs/en/Timing.md +++ b/docs/en/Timing.md @@ -10,7 +10,7 @@ ABP provides a basic infrastructure to make it easy and handle automatically whe `DateTime.Now` returns a `DateTime` object with the **local date & time of the server**. A `DateTime` object **doesn't store the time zone information**. So, you can not know the **absolute date & time** stored in this object. You can only make **assumptions**, like assuming that it was created in UTC+05 time zone. The things especially gets complicated when you save this value to a database and read later, or send it to a client in a **different time zone**. -One solution to this problem is always use `DateTime.UtcNow` and assume all `DateTime` objects as UTC time. In this was, you can convert it to the time zone of the target client when needed. +One solution to this problem is always use `DateTime.UtcNow` and assume all `DateTime` objects as UTC time. In this way, you can convert it to the time zone of the target client when needed. `IClock` provides an abstraction while getting the current time, so you can control the kind of the date time (UTC or local) in a single point in your application. @@ -110,4 +110,4 @@ See the [setting documentation](Settings.md) to learn more about the setting sys `ITimezoneProvider` is a service to simple convert [Windows Time Zone Id](https://support.microsoft.com/en-us/help/973627/microsoft-time-zone-index-values) values to [Iana Time Zone Name](https://www.iana.org/time-zones) values and vice verse. It also provides methods to get list of these time zones and get a `TimeZoneInfo` with a given name. -It has been implemented using the [TimeZoneConverter](https://github.com/mj1856/TimeZoneConverter) library. \ No newline at end of file +It has been implemented using the [TimeZoneConverter](https://github.com/mj1856/TimeZoneConverter) library. diff --git a/docs/en/UI/Angular/Multi-Tenancy.md b/docs/en/UI/Angular/Multi-Tenancy.md index 94b3954a03..fb9fb54f76 100644 --- a/docs/en/UI/Angular/Multi-Tenancy.md +++ b/docs/en/UI/Angular/Multi-Tenancy.md @@ -1,4 +1,4 @@ -# Multi Tenancy in Angular UI +# Multi Tenancy in Angular UI ABP Angular UI supports the multi-tenancy. The following features related to multi-tenancy are available in the startup templates. @@ -17,7 +17,7 @@ On the page above, you can; ![Tenant Switching Component](./images/tenant-switching-box.png)

Tenant Switching Component

-You can switch between existing tenants by using the tenant switching component in the child pages of the `AccountLayoutComponent` (like Login page). Angular UI sends the selected tenant id sends to the backend as `__tenant` header on each request. +You can switch between existing tenants by using the tenant switching component in the child pages of the `AccountLayoutComponent` (like Login page). Angular UI sends the selected tenant id to the backend as `__tenant` header on each request. ## Domain Tenant Resolver diff --git a/docs/en/UI/Angular/Permission-Management.md b/docs/en/UI/Angular/Permission-Management.md index 8cc7325afd..b9c4649c03 100644 --- a/docs/en/UI/Angular/Permission-Management.md +++ b/docs/en/UI/Angular/Permission-Management.md @@ -4,36 +4,17 @@ A permission is a simple policy that is granted or prohibited for a particular u You can get permission of authenticated user using `getGrantedPolicy` selector of `ConfigState`. -You can get permission as boolean value from store: - -```js -import { Store } from '@ngxs/store'; -import { ConfigState } from '@abp/ng.core'; - -export class YourComponent { - constructor(private store: Store) {} - - ngOnInit(): void { - const canCreate = this.store.selectSnapshot(ConfigState.getGrantedPolicy('AbpIdentity.Roles.Create')); - } - - // ... -} -``` - -Or you can get it via `ConfigStateService`: +You can get permission as boolean value: ```js import { ConfigStateService } from '@abp/ng.core'; export class YourComponent { - constructor(private configStateService: ConfigStateService) {} + constructor(private config: ConfigStateService) {} ngOnInit(): void { - const canCreate = this.configStateService.getGrantedPolicy('AbpIdentity.Roles.Create'); + const canCreate = this.config.getGrantedPolicy('AbpIdentity.Roles.Create'); } - - // ... } ``` diff --git a/docs/en/UI/AspNetCore/Customization-User-Interface.md b/docs/en/UI/AspNetCore/Customization-User-Interface.md index a68829cf09..f1f0174c89 100644 --- a/docs/en/UI/AspNetCore/Customization-User-Interface.md +++ b/docs/en/UI/AspNetCore/Customization-User-Interface.md @@ -158,7 +158,7 @@ Just as explained above, you can replace any component, layout or c# class of th ## Overriding Static Resources -Overriding a static embedded resource (like JavaScript, Css or image files) of a module is pretty easy. Just place a file in the same path in your solution and let the Virtual File System to handle it. +Overriding a static embedded resource (like JavaScript, Css or image files) of a module is pretty easy. Just place a file in the same path in your solution and let the [Virtual File System](../../Virtual-File-System.md) to handle it. ## Manipulating the Bundles diff --git a/docs/en/Virtual-File-System.md b/docs/en/Virtual-File-System.md index a519b02ec6..307c6e92bc 100644 --- a/docs/en/Virtual-File-System.md +++ b/docs/en/Virtual-File-System.md @@ -6,7 +6,7 @@ The Virtual File System makes it possible to manage files that do not physically > Most of the times you don't need to manually install this package since it comes pre-installed with the [application startup template](Startup-Templates/Application.md). -[Volo.Abp.VirtualFileSystem](https://www.nuget.org/packages/Volo.Abp.VirtualFileSystem) is the main page of the Virtual File System. +[Volo.Abp.VirtualFileSystem](https://www.nuget.org/packages/Volo.Abp.VirtualFileSystem) is the main package of the Virtual File System. Use the ABP CLI to add this package to your project: @@ -18,9 +18,9 @@ If you want to do it manually, install the [Volo.Abp.VirtualFileSystem](https:// ## Working with the Embedded Files -### Embed the Files +### Embedding the Files -A file should be first marked as an **embedded resource** to embed the file into the assembly. The easiest way to do it is to select the file from the **Solution Explorer** and set **Build Action** to **Embedded Resource** from the **Properties** window. Example: +A file should be first marked as **embedded resource** to embed the file into the assembly. The easiest way to do it is to select the file from the **Solution Explorer** and set **Build Action** to **Embedded Resource** from the **Properties** window. Example: ![build-action-embedded-resource-sample](images/build-action-embedded-resource-sample.png) @@ -55,21 +55,21 @@ Configure(options => }); ```` -The `AddEmbedded` extension method takes a class, finds all embedded files from the **assembly of the given class** and registers them to the virtual file system. It is common to pass the module class as the generic argument. +The `AddEmbedded` extension method takes a class, finds all embedded files from the **assembly of the given class** and registers them to the virtual file system. `AddEmbedded` can get two optional parameters; * `baseNamespace`: This may only needed if you didn't configure the `GenerateEmbeddedFilesManifest` step explained above and your root namespace is not empty. In this case, set your root namespace here. -* `baseFolder`: If you don't want to expose all embedded files in the project, but only want to expose a specific folder (and sub folders/files), then you can set the base folder relative to your project root page. +* `baseFolder`: If you don't want to expose all embedded files in the project, but only want to expose a specific folder (and sub folders/files), then you can set the base folder relative to your project root folder. -**Example: Add files under the `MyFiles` folder in the project** +**Example: Add files under the `MyResources` folder in the project** ````csharp Configure(options => { options.FileSets.AddEmbedded( - baseNamespace: "Acme.BookStore.MyFiles", - baseFolder: "/MyFiles" + baseNamespace: "Acme.BookStore", + baseFolder: "/MyResources" ); }); ```` @@ -77,56 +77,15 @@ Configure(options => This example assumes; * Your project root (default) namespace is `Acme.BookStore`. -* Your project has a folder, named `MyFiles` -* You only want to add `MyFiles` folder to the virtual file system. +* Your project has a folder, named `MyResources` +* You only want to add `MyResources` folder to the virtual file system. -### Dealing With Embedded Files During Development +### IVirtualFileProvider -Embedding a file into an assembly and being able to use it from another project just by referencing the assembly (or adding a NuGet package) is invaluable for creating a re-usable module. However, it makes it a little bit harder to develop the module itself. - -Let's assume that you're developing a module that contains an embedded JavaScript file. Whenever you change this file you must re-compile the project, re-start the application and refresh the browser page to take the change. Obviously, this is very time consuming and tedious. - -What is needed is the ability for the application to directly use the physical file at development time and a have a browser refresh reflect any change made in the JavaScript file. The `ReplaceEmbeddedByPhysical` method makes all this possible. - -The example below shows an application that depends on a module (`MyModule`) that itself contains embedded files. The application can reach the source code of the module at development time. +After embedding a file into an assembly and registering it to the virtual file system, the `IVirtualFileProvider` interface can be used to get the file or directory contents: ````C# -[DependsOn(typeof(MyModule))] -public class MyWebAppModule : AbpModule -{ - public override void ConfigureServices(ServiceConfigurationContext context) - { - var hostingEnvironment = context.Services.GetHostingEnvironment(); - - if (hostingEnvironment.IsDevelopment()) //only for development time - { - Configure(options => - { - options.FileSets.ReplaceEmbeddedByPhysical( - Path.Combine( - hostingEnvironment.ContentRootPath, - string.Format( - "..{0}MyModuleProject", - Path.DirectorySeparatorChar - ) - ) - ); - }); - } - } -} -```` - -The code above assumes that `MyWebAppModule` and `MyModule` are two different projects in a Visual Studio solution and `MyWebAppModule` depends on the `MyModule`. - -> The [application startup template](Startup-Templates/Application.md) already uses this technique for the localization files. So, when you change a localization file it automatically detects the change. - -## IVirtualFileProvider - -After embedding a file into an assembly and registering it to the virtual file system, the `IVirtualFileProvider` interface can be used to get files or directory contents: - -````C# -public class MyService +public class MyService : ITransientDependency { private readonly IVirtualFileProvider _virtualFileProvider; @@ -135,7 +94,7 @@ public class MyService _virtualFileProvider = virtualFileProvider; } - public void Foo() + public void Test() { //Getting a single file var file = _virtualFileProvider @@ -160,14 +119,14 @@ The Virtual File System is well integrated to ASP.NET Core: ### UseVirtualFiles Middleware -The Virtual Files Middleware is used to serve embedded (js, css, image...) files to clients/browsers just like physical files in the **wwwroot** folder. Add it just after the static file middleware as shown below: +The Virtual Files Middleware is used to serve embedded (js, css, image...) files to clients/browsers just like physical files in the **wwwroot** folder. It also covers the physical files. + +Replace the `app.UseStaticFiles()` with the `app.UseVirtualFiles()` in your ASP.NET Core middleware configuration: ````C# app.UseVirtualFiles(); ```` -Adding virtual files middleware after the static files middleware makes it possible to override a virtual file with a real physical file simply by placing it in the same location as the virtual file. - > `UseVirtualFiles()` is already configured for the [application startup template](Startup-Templates/Application.md). #### Static Virtual File Folders @@ -176,6 +135,62 @@ By default, ASP.NET Core only allows the `wwwroot` folder to contain the static * Pages * Views +* Components * Themes -This allows to add `.js`, `.css`... files near to your `.cshtml` file that is easier to develop and maintain your project. \ No newline at end of file +This allows to add `.js`, `.css`... files near to your `.cshtml` file that is easier to develop and maintain your project. + +## Dealing With Embedded Files During Development + +Embedding a file into an assembly and being able to use it from another project just by referencing the assembly (or adding a NuGet package) is invaluable for creating a re-usable module. However, it makes it a little bit harder to develop the module itself. + +Let's assume that you're developing a module that contains an embedded JavaScript file. Whenever you change this file you must re-compile the project, re-start the application and refresh the browser page to take the change. Obviously, this is very time consuming and tedious. + +What is needed is the ability for the application to directly use the physical file at development time and a browser refresh reflects any change made in the JavaScript file. The `ReplaceEmbeddedByPhysical` method makes all this possible. + +The example below shows an application that depends on a module (`MyModule`) that contains embedded files. The application can access to the source code of the module at development time. + +````C# +[DependsOn(typeof(MyModule))] +public class MyWebAppModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + var hostingEnvironment = context.Services.GetHostingEnvironment(); + + if (hostingEnvironment.IsDevelopment()) //only for development time + { + Configure(options => + { + options.FileSets.ReplaceEmbeddedByPhysical( + Path.Combine( + hostingEnvironment.ContentRootPath, + string.Format( + "..{0}MyModuleProject", + Path.DirectorySeparatorChar + ) + ) + ); + }); + } + } +} +```` + +The code above assumes that `MyWebAppModule` and `MyModule` are two different projects in a Visual Studio solution and `MyWebAppModule` depends on the `MyModule`. + +> The [application startup template](Startup-Templates/Application.md) already uses this technique for the localization files. So, when you change a localization file it automatically detects the change. + +## Replacing/Overriding Virtual Files + +Virtual File System creates a unified file system on runtime, where the actual files are distributed into different modules in the development time. + +If two modules adds a file to the same virtual path (like `my-path/my-file.css`), the one added later overrides/replaces the previous one ([module dependency](Module-Development-Basics.md) order determines the order of the files being added). + +This feature allows your application to override/replace any virtual file defined a module that is used by your application. This is one of the fundamental extensibility features of the ABP Framework. + +So, if you need to replace a file of a module, just create the file in the exactly same path in your module/application + +### Physical Files + +Physical files always override the virtual files. That means if you put a file under the `/wwwroot/my-folder/my-file.css`, it will override the file in the same location of the virtual file system. So, you need to know the file paths defined in the modules to override them. \ No newline at end of file diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 9bf7083d0d..660f2b1b60 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -214,6 +214,19 @@ "text": "Object to object mapping", "path": "Object-To-Object-Mapping.md" }, + { + "text": "Email Sending", + "items": [ + { + "text": "Email Sending System", + "path": "Emailing.md", + }, + { + "text": "MailKit Integration", + "path": "MailKit.md", + } + ] + }, { "text": "BLOB Storing", "items": [ @@ -651,6 +664,10 @@ "text": "Microservice Architecture", "path": "Microservice-Architecture.md" }, + { + "text": "Preview Releases", + "path": "Previews.md" + }, { "text": "Nightly Builds", "path": "Nightly-Builds.md" diff --git a/docs/en/images/replace-email-layout.png b/docs/en/images/replace-email-layout.png new file mode 100644 index 0000000000..67db895f92 Binary files /dev/null and b/docs/en/images/replace-email-layout.png differ diff --git a/docs/en/images/solution-files-non-mvc.png b/docs/en/images/solution-files-non-mvc.png index 880cf20d46..323991b95c 100644 Binary files a/docs/en/images/solution-files-non-mvc.png and b/docs/en/images/solution-files-non-mvc.png differ diff --git a/docs/zh-Hans/Background-Jobs-Quartz.md b/docs/zh-Hans/Background-Jobs-Quartz.md index 61de4548f5..427276baaf 100644 --- a/docs/zh-Hans/Background-Jobs-Quartz.md +++ b/docs/zh-Hans/Background-Jobs-Quartz.md @@ -70,7 +70,43 @@ public class YourModule : AbpModule } ```` -Quartz**默认**将作业与调度信息存储在**内存**中,示例中我们使用[选项模式](Options.md)的预配置将其更改为存储到数据库中. 有关Quartz的更多配置请参阅[Quartz文档](https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/index.html). +从ABP3.1版本开始,我们在 `AbpQuartzOptions` 添加了 `Configurator` 用于配置Quartz. 例: + +````csharp +[DependsOn( + //...other dependencies + typeof(AbpBackgroundJobsQuartzModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + + PreConfigure(options => + { + options.Configurator = configure => + { + configure.UsePersistentStore(storeOptions => + { + storeOptions.UseProperties = true; + storeOptions.UseJsonSerializer(); + storeOptions.UseSqlServer(configuration.GetConnectionString("Quartz")); + storeOptions.UseClustering(c => + { + c.CheckinMisfireThreshold = TimeSpan.FromSeconds(20); + c.CheckinInterval = TimeSpan.FromSeconds(10); + }); + }); + }; + }); + } +} +```` + +> 你可以选择你喜爱的方式来配置Quaratz. + +Quartz**默认**将作业与调度信息存储在**内存**中,示例中我们使用[选项模式](Options.md)的预配置将其更改为存储到数据库中. 有关Quartz的更多配置请参阅[Quartz文档](https://www.quartz-scheduler.net/). ## 异常处理 diff --git a/docs/zh-Hans/CLI.md b/docs/zh-Hans/CLI.md index 4af252e89f..82a72ec7ed 100644 --- a/docs/zh-Hans/CLI.md +++ b/docs/zh-Hans/CLI.md @@ -79,7 +79,7 @@ abp new Acme.BookStore * `--separate-identity-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点. * `none`: 无UI. 这个模板还有一些额外的选项: * `--separate-identity-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点. - * `--mobile` 或者 `-m`: 指定移动应用程序框架. 默认框架是 `react-native`. 其他选项: + * `--mobile` 或者 `-m`: 指定移动应用程序框架. 如果未指定,则不会创建任何移动应用程序,其他选项: * `none`: 不包含移动应用程序. * `react-native`: React Native. * `--database-provider` 或者 `-d`: 指定数据库提供程序.默认是 `ef`.其他选项: @@ -251,4 +251,4 @@ abp help [命令名] ````bash abp help # 显示常规帮助. abp help new # 显示有关 "New" 命令的帮助. -```` \ No newline at end of file +```` diff --git a/docs/zh-Hans/Exception-Handling.md b/docs/zh-Hans/Exception-Handling.md index 3d8408b26d..63b95643f0 100644 --- a/docs/zh-Hans/Exception-Handling.md +++ b/docs/zh-Hans/Exception-Handling.md @@ -300,3 +300,14 @@ services.Configure(options => - 如果请求的实体不存在,则抛出`EntityNotFoundException` 异常. 此异常大多数由 [repositories](Repositories.md) 抛出. 你同样可以在代码中抛出这些类型的异常(虽然很少需要这样做) + +## 发送异常详情到客户端 + +你可以通过 `AbpExceptionHandlingOptions` 类的 `SendExceptionsDetailsToClients` 属性异常发送到客户端: + +````csharp +services.Configure(options => +{ + options.SendExceptionsDetailsToClients = true; +}); +```` diff --git a/docs/zh-Hans/Getting-Started-React-Native.md b/docs/zh-Hans/Getting-Started-React-Native.md index 54d093cb7c..225a0f3a13 100644 --- a/docs/zh-Hans/Getting-Started-React-Native.md +++ b/docs/zh-Hans/Getting-Started-React-Native.md @@ -2,7 +2,7 @@ ABP平台提供了[React Native](https://reactnative.dev/)模板用于开发移动应用程序. -当你按照[入门文档](Getting-Started.md)中所述**创建新应用程序**时,解决方案默认将React Native应用程序包含在 `react-native` 文件夹中. +当你按照[入门文档](Getting-Started.md)中所述**创建新应用程序**时, 你应该使用`-m react-native`选项以在解决方案中包含`react-native`项目. ## 配置你的本地IP地址 @@ -67,4 +67,4 @@ yarn start 输入用户名 **admin**,密码 **1q2w3E*** 登录到应用程序. -应用程序已经启动并执行,你可以基于该启动模板开发应用程序. \ No newline at end of file +应用程序已经启动并执行,你可以基于该启动模板开发应用程序. diff --git a/docs/zh-Hans/Getting-Started.md b/docs/zh-Hans/Getting-Started.md index fe1a9ec6f9..bf46816d32 100644 --- a/docs/zh-Hans/Getting-Started.md +++ b/docs/zh-Hans/Getting-Started.md @@ -62,10 +62,10 @@ dotnet tool update -g Volo.Abp.Cli 使用ABP CLI的 `new` 命令创建新项目: ````shell -abp new Acme.BookStore{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}} +abp new Acme.BookStore{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}} --mobile react-native ```` -* 此命令还会在解决方案文件夹内创建一个React Native移动应用程序. 如果你不想要它,可以安全地删除它,或者在 `abp new` 命令中指定 `-m none` 选项,以使其完全不包含在解决方案中. +* 此命令还会在解决方案文件夹内创建一个React Native移动应用程序. 如果你不想要它,可以安全地删除它或从`abp new`命令中删除`--mobile react-native`选项, 以使其完全不包含在解决方案中. {{ if UI == "NG" }} @@ -351,11 +351,8 @@ yarn start #### 移动开发 -当你创建一个新的应用程序时.该解决方案默认包含 `react-native`文件夹. 这是一个基础的[React Native](https://reactnative.dev/)启动模板,用于开发与基于ABP的后端集成的移动应用程序. +当你创建一个新的应用程序时. 可以添加`-m react-native`选项以在解决方案中包含 `react-native`项目. 这是一个基础的[React Native](https://reactnative.dev/)启动模板,用于开发与基于ABP的后端集成的移动应用程序. -如果你不计划使用React Native开发移动应用程序,你可以忽略并删除 `react-native` 文件夹. - -> 你可以在ABP CLI中指定 `-m none` 选项,以使 `react-native` 目录完全不包含在解决方案中 请参阅"[React Native入门](Getting-Started-React-Native.md)"文档了解如何配置和运行React Native应用程序. diff --git a/docs/zh-Hans/Startup-Templates/Application.md b/docs/zh-Hans/Startup-Templates/Application.md index 44ea5adfaf..534a26d8fb 100644 --- a/docs/zh-Hans/Startup-Templates/Application.md +++ b/docs/zh-Hans/Startup-Templates/Application.md @@ -57,6 +57,20 @@ abp new Acme.BookStore -u angular abp new Acme.BookStore -d mongodb ```` +### 指定移动应用程序框架 + +该模板支持以下移动应用程序框架: + +- `react-native`: React Native + +使用 `-m` (or `--mobile`) 选项来指定移动应用程序框架: + +````bash +abp new Acme.BookStore -m react-native +```` + +如果未指定, 则不会创建任何移动应用程序. + ## 解决方案结构 根据命令的选项,会创建略有不同的解决方案结构. @@ -342,7 +356,7 @@ Home模块是一个可延迟加载的模块, 它加载应用程序的根地址. ### React Native -解决方案将[React Native](https://reactnative.dev/)应用程序作为默认值包含在 `react-native` 文件夹中. +如果使用 `-m react-native` 选项解决方案将[React Native](https://reactnative.dev/)应用程序作为默认值包含在 `react-native` 文件夹中. 服务器端类似于上面描述的解决方案. `*.HttpApi.Host` 的项目提供 API, 所以 React 本机应用程序使用它. diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 5778b054ed..3811a80ee8 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -323,6 +323,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Aws", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Aws.Tests", "test\Volo.Abp.BlobStoring.Aws.Tests\Volo.Abp.BlobStoring.Aws.Tests.csproj", "{2CD3B26A-CA81-4279-8D5D-6A594517BB3F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.GlobalFeatures", "src\Volo.Abp.GlobalFeatures\Volo.Abp.GlobalFeatures.csproj", "{04F44063-C952-403A-815F-EFB778BDA125}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.GlobalFeatures.Tests", "test\Volo.Abp.GlobalFeatures.Tests\Volo.Abp.GlobalFeatures.Tests.csproj", "{231F1581-AA21-44C3-BF27-51EB3AD5355C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -961,6 +965,14 @@ Global {2CD3B26A-CA81-4279-8D5D-6A594517BB3F}.Debug|Any CPU.Build.0 = Debug|Any CPU {2CD3B26A-CA81-4279-8D5D-6A594517BB3F}.Release|Any CPU.ActiveCfg = Release|Any CPU {2CD3B26A-CA81-4279-8D5D-6A594517BB3F}.Release|Any CPU.Build.0 = Release|Any CPU + {04F44063-C952-403A-815F-EFB778BDA125}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04F44063-C952-403A-815F-EFB778BDA125}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04F44063-C952-403A-815F-EFB778BDA125}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04F44063-C952-403A-815F-EFB778BDA125}.Release|Any CPU.Build.0 = Release|Any CPU + {231F1581-AA21-44C3-BF27-51EB3AD5355C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {231F1581-AA21-44C3-BF27-51EB3AD5355C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {231F1581-AA21-44C3-BF27-51EB3AD5355C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {231F1581-AA21-44C3-BF27-51EB3AD5355C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1124,6 +1136,8 @@ Global {8E49687A-E69F-49F2-8DB0-428D0883A937} = {447C8A77-E5F0-4538-8687-7383196D04EA} {50968CDE-1029-4051-B2E5-B69D0ECF2A18} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {2CD3B26A-CA81-4279-8D5D-6A594517BB3F} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {04F44063-C952-403A-815F-EFB778BDA125} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {231F1581-AA21-44C3-BF27-51EB3AD5355C} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj index 906f4fc2c9..437ae8c45d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj @@ -20,8 +20,12 @@ + + + + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/AbpMvcUiOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/AbpMvcUiOptions.cs new file mode 100644 index 0000000000..d585ad3965 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/AbpMvcUiOptions.cs @@ -0,0 +1,15 @@ +namespace Volo.Abp.AspNetCore.Mvc.UI +{ + public class AbpMvcUiOptions + { + /// + /// Default value: "/Account/Login". + /// + public string LoginUrl { get; set; } = "/Account/Login"; + + /// + /// Default value: "/Account/Logout". + /// + public string LogoutUrl { get; set; } = "/Account/Logout"; + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj index e190116b2f..e564dec738 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj @@ -21,6 +21,7 @@ + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs index e3cee4e72b..88a81fa1ec 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs @@ -10,14 +10,12 @@ using Microsoft.Extensions.Options; using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Net; using System.Reflection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.DataAnnotations; -using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Routing; @@ -34,6 +32,7 @@ using Volo.Abp.AspNetCore.VirtualFileSystem; using Volo.Abp.DependencyInjection; using Volo.Abp.Http; using Volo.Abp.DynamicProxy; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Http.Modeling; using Volo.Abp.Localization; using Volo.Abp.Modularity; @@ -46,7 +45,8 @@ namespace Volo.Abp.AspNetCore.Mvc typeof(AbpLocalizationModule), typeof(AbpApiVersioningAbstractionsModule), typeof(AbpAspNetCoreMvcContractsModule), - typeof(AbpUiModule) + typeof(AbpUiModule), + typeof(AbpGlobalFeaturesModule) )] public class AbpAspNetCoreMvcModule : AbpModule { @@ -168,7 +168,7 @@ namespace Volo.Abp.AspNetCore.Mvc context.Services.Replace(ServiceDescriptor.Singleton()); context.Services.AddSingleton(); - + Configure(mvcOptions => { mvcOptions.AddAbp(context.Services); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs index 59166d3e1d..a2f1b1b523 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs @@ -4,6 +4,7 @@ using Volo.Abp.AspNetCore.Mvc.Auditing; using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.AspNetCore.Mvc.ExceptionHandling; using Volo.Abp.AspNetCore.Mvc.Features; +using Volo.Abp.AspNetCore.Mvc.GlobalFeatures; using Volo.Abp.AspNetCore.Mvc.ModelBinding; using Volo.Abp.AspNetCore.Mvc.Response; using Volo.Abp.AspNetCore.Mvc.Uow; @@ -29,6 +30,7 @@ namespace Volo.Abp.AspNetCore.Mvc private static void AddActionFilters(MvcOptions options) { + options.Filters.AddService(typeof(GlobalFeatureActionFilter)); options.Filters.AddService(typeof(AbpAuditActionFilter)); options.Filters.AddService(typeof(AbpNoContentActionFilter)); options.Filters.AddService(typeof(AbpFeatureActionFilter)); @@ -39,6 +41,7 @@ namespace Volo.Abp.AspNetCore.Mvc private static void AddPageFilters(MvcOptions options) { + options.Filters.AddService(typeof(GlobalFeaturePageFilter)); options.Filters.AddService(typeof(AbpExceptionPageFilter)); options.Filters.AddService(typeof(AbpAuditPageFilter)); options.Filters.AddService(typeof(AbpFeaturePageFilter)); @@ -58,4 +61,4 @@ namespace Volo.Abp.AspNetCore.Mvc ); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs index 0254a6022b..f19de585b9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.Options; using Volo.Abp.Application.Services; using Volo.Abp.DependencyInjection; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Http; using Volo.Abp.Http.Modeling; using Volo.Abp.Http.ProxyScripting.Generators; @@ -142,18 +143,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions if (controller.ApiExplorer.IsVisible == null) { - var controllerType = controller.ControllerType.AsType(); - var remoteServiceAtt = ReflectionHelper.GetSingleAttributeOrDefault(controllerType.GetTypeInfo()); - if (remoteServiceAtt != null) - { - controller.ApiExplorer.IsVisible = - remoteServiceAtt.IsEnabledFor(controllerType) && - remoteServiceAtt.IsMetadataEnabledFor(controllerType); - } - else - { - controller.ApiExplorer.IsVisible = true; - } + controller.ApiExplorer.IsVisible = IsVisibleRemoteService(controller.ControllerType); } foreach (var action in controller.Actions) @@ -164,16 +154,18 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions protected virtual void ConfigureApiExplorer(ActionModel action) { - if (action.ApiExplorer.IsVisible == null) + if (action.ApiExplorer.IsVisible != null) { - var remoteServiceAtt = ReflectionHelper.GetSingleAttributeOrDefault(action.ActionMethod); - if (remoteServiceAtt != null) - { - action.ApiExplorer.IsVisible = - remoteServiceAtt.IsEnabledFor(action.ActionMethod) && - remoteServiceAtt.IsMetadataEnabledFor(action.ActionMethod); - } + return; } + + var visible = IsVisibleRemoteServiceMethod(action.ActionMethod); + if (visible == null) + { + return; + } + + action.ApiExplorer.IsVisible = visible; } protected virtual void ConfigureSelector(ControllerModel controller, [CanBeNull] ConventionalControllerSetting configuration) @@ -397,5 +389,45 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions { return typeof(IRemoteService).GetTypeInfo().IsAssignableFrom(controllerType); } + + protected virtual bool IsVisibleRemoteService(Type controllerType) + { + if (!IsGlobalFeatureEnabled(controllerType)) + { + return false; + } + + var attribute = ReflectionHelper.GetSingleAttributeOrDefault(controllerType); + if (attribute == null) + { + return true; + } + + return attribute.IsEnabledFor(controllerType) && + attribute.IsMetadataEnabledFor(controllerType); + } + + protected virtual bool? IsVisibleRemoteServiceMethod(MethodInfo method) + { + var attribute = ReflectionHelper.GetSingleAttributeOrDefault(method); + if (attribute == null) + { + return null; + } + + return attribute.IsEnabledFor(method) && + attribute.IsMetadataEnabledFor(method); + } + + protected virtual bool IsGlobalFeatureEnabled(Type controllerType) + { + var attribute = ReflectionHelper.GetSingleAttributeOrDefault(controllerType); + if (attribute == null) + { + return true; + } + + return GlobalFeatureManager.Instance.IsEnabled(attribute.GetFeatureName()); + } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeatureActionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeatureActionFilter.cs new file mode 100644 index 0000000000..e3fa489115 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeatureActionFilter.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.DependencyInjection; +using Volo.Abp.GlobalFeatures; +using Volo.Abp.Reflection; + +namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures +{ + public class GlobalFeatureActionFilter : IAsyncActionFilter, ITransientDependency + { + public ILogger Logger { get; set; } + + public GlobalFeatureActionFilter() + { + Logger = NullLogger.Instance; + } + + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + if (!context.ActionDescriptor.IsControllerAction()) + { + await next(); + return; + } + + if (!IsGlobalFeatureEnabled(context.Controller.GetType(), out var attribute)) + { + Logger.LogWarning($"The '{context.Controller.GetType().FullName}' controller needs to enable '{attribute.Name}' feature."); + context.Result = new NotFoundResult(); + return; + } + + await next(); + } + + protected virtual bool IsGlobalFeatureEnabled(Type controllerType, out RequiresGlobalFeatureAttribute attribute) + { + attribute = ReflectionHelper.GetSingleAttributeOrDefault(controllerType); + return attribute == null || GlobalFeatureManager.Instance.IsEnabled(attribute.GetFeatureName()); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeaturePageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeaturePageFilter.cs new file mode 100644 index 0000000000..d29c44b4af --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeaturePageFilter.cs @@ -0,0 +1,52 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.DependencyInjection; +using Volo.Abp.GlobalFeatures; +using Volo.Abp.Reflection; + +namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures +{ + public class GlobalFeaturePageFilter: IAsyncPageFilter, ITransientDependency + { + public ILogger Logger { get; set; } + + public GlobalFeaturePageFilter() + { + Logger = NullLogger.Instance; + } + + public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) + { + return Task.CompletedTask; + } + + public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + { + if (context.HandlerInstance == null || !context.ActionDescriptor.IsPageAction()) + { + await next(); + return; + } + + if (!IsGlobalFeatureEnabled(context.HandlerInstance.GetType(), out var attribute)) + { + Logger.LogWarning($"The '{context.HandlerInstance.GetType().FullName}' page needs to enable '{attribute.Name}' feature."); + context.Result = new NotFoundResult(); + return; + } + + await next(); + } + + protected virtual bool IsGlobalFeatureEnabled(Type controllerType, out RequiresGlobalFeatureAttribute attribute) + { + attribute = ReflectionHelper.GetSingleAttributeOrDefault(controllerType); + return attribute == null || GlobalFeatureManager.Instance.IsEnabled(attribute.GetFeatureName()); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/DefaultAbpRequestLocalizationOptionsProvider.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/DefaultAbpRequestLocalizationOptionsProvider.cs index 64f339ffce..263d0532d0 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/DefaultAbpRequestLocalizationOptionsProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/DefaultAbpRequestLocalizationOptionsProvider.cs @@ -50,35 +50,38 @@ namespace Microsoft.AspNetCore.RequestLocalization { using (await _syncSemaphore.LockAsync()) { - using (var serviceScope = _serviceProviderFactory.CreateScope()) + if (_requestLocalizationOptions == null) { - var languageProvider = serviceScope.ServiceProvider.GetRequiredService(); - var settingProvider = serviceScope.ServiceProvider.GetRequiredService(); + using (var serviceScope = _serviceProviderFactory.CreateScope()) + { + var languageProvider = serviceScope.ServiceProvider.GetRequiredService(); + var settingProvider = serviceScope.ServiceProvider.GetRequiredService(); - var languages = await languageProvider.GetLanguagesAsync(); - var defaultLanguage = await settingProvider.GetOrNullAsync(LocalizationSettingNames.DefaultLanguage); + var languages = await languageProvider.GetLanguagesAsync(); + var defaultLanguage = await settingProvider.GetOrNullAsync(LocalizationSettingNames.DefaultLanguage); - var options = !languages.Any() - ? new RequestLocalizationOptions() - : new RequestLocalizationOptions - { - DefaultRequestCulture = DefaultGetRequestCulture(defaultLanguage, languages), + var options = !languages.Any() + ? new RequestLocalizationOptions() + : new RequestLocalizationOptions + { + DefaultRequestCulture = DefaultGetRequestCulture(defaultLanguage, languages), - SupportedCultures = languages - .Select(l => l.CultureName) - .Distinct() - .Select(c => new CultureInfo(c)) - .ToArray(), + SupportedCultures = languages + .Select(l => l.CultureName) + .Distinct() + .Select(c => new CultureInfo(c)) + .ToArray(), - SupportedUICultures = languages - .Select(l => l.UiCultureName) - .Distinct() - .Select(c => new CultureInfo(c)) - .ToArray() - }; + SupportedUICultures = languages + .Select(l => l.UiCultureName) + .Distinct() + .Select(c => new CultureInfo(c)) + .ToArray() + }; - _optionsAction?.Invoke(options); - _requestLocalizationOptions = options; + _optionsAction?.Invoke(options); + _requestLocalizationOptions = options; + } } } } @@ -98,4 +101,4 @@ namespace Microsoft.AspNetCore.RequestLocalization return new RequestCulture(cultureName, uiCultureName); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingOptions.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingOptions.cs new file mode 100644 index 0000000000..0cfdb8089d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingOptions.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.AspNetCore.ExceptionHandling +{ + public class AbpExceptionHandlingOptions + { + public bool SendExceptionsDetailsToClients { get; set; } = false; + } +} diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs index 5bf59b0cc9..cd2f393104 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs @@ -19,20 +19,21 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling { public class DefaultExceptionToErrorInfoConverter : IExceptionToErrorInfoConverter, ITransientDependency { - public bool SendAllExceptionsToClients { get; set; } = false; - protected AbpExceptionLocalizationOptions LocalizationOptions { get; } + protected AbpExceptionHandlingOptions ExceptionHandlingOptions { get; } protected IStringLocalizerFactory StringLocalizerFactory { get; } protected IStringLocalizer L { get; } protected IServiceProvider ServiceProvider { get; } public DefaultExceptionToErrorInfoConverter( IOptions localizationOptions, + IOptions exceptionHandlingOptions, IStringLocalizerFactory stringLocalizerFactory, IStringLocalizer abpUiStringLocalizer, IServiceProvider serviceProvider) { ServiceProvider = serviceProvider; + ExceptionHandlingOptions = exceptionHandlingOptions.Value; StringLocalizerFactory = stringLocalizerFactory; L = abpUiStringLocalizer; LocalizationOptions = localizationOptions.Value; @@ -52,7 +53,7 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling protected virtual RemoteServiceErrorInfo CreateErrorInfoWithoutCode(Exception exception) { - if (SendAllExceptionsToClients) + if (ExceptionHandlingOptions.SendExceptionsDetailsToClients) { return CreateDetailedErrorInfoFromException(exception); } @@ -293,4 +294,4 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling return detailBuilder.ToString(); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AbpAspNetCoreContentOptions.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AbpAspNetCoreContentOptions.cs index f4b7d631cc..92961971fb 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AbpAspNetCoreContentOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AbpAspNetCoreContentOptions.cs @@ -400,7 +400,8 @@ namespace Volo.Abp.AspNetCore.VirtualFileSystem { "/Pages", "/Views", - "/Themes" + "/Themes", + "/Components" }; AllowedExtraWebContentFileExtensions = ContentTypeMaps.Select(x => x.Key).ToList(); diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditScope.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditLogScope.cs similarity index 100% rename from framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditScope.cs rename to framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditLogScope.cs diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index 4a713ceb9e..d5ece4361f 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -294,7 +294,7 @@ namespace Volo.Abp.Cli.Commands protected virtual MobileApp GetMobilePreference(CommandLineArgs commandLineArgs) { var optionValue = commandLineArgs.Options.GetOrNull(Options.Mobile.Short, Options.Mobile.Long); - var template = commandLineArgs.Options.GetOrNull(Options.Template.Short, Options.Template.Long); + switch (optionValue) { case "none": @@ -302,7 +302,7 @@ namespace Volo.Abp.Cli.Commands case "react-native": return MobileApp.ReactNative; default: - return ConsoleTemplate.TemplateName == template ? MobileApp.None : MobileApp.ReactNative; + return MobileApp.None; } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs index 5fe255828e..5fd30115e2 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs @@ -53,9 +53,6 @@ namespace Volo.Abp.Cli.Commands private async Task UpdateNugetPackages(CommandLineArgs commandLineArgs, string directory) { - var includePreviews = commandLineArgs - .Options - .GetOrNull(Options.IncludePreviews.Short, Options.IncludePreviews.Long) != null; var solution = commandLineArgs.Options.GetOrNull(Options.SolutionName.Short, Options.SolutionName.Long); if (solution.IsNullOrWhiteSpace()) @@ -69,7 +66,7 @@ namespace Volo.Abp.Cli.Commands { var solutionName = Path.GetFileName(solution).RemovePostFix(".sln"); - await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, includePreviews, checkAll: checkAll); + await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, checkAll: checkAll); Logger.LogInformation($"Volo packages are updated in {solutionName} solution."); return; @@ -81,7 +78,7 @@ namespace Volo.Abp.Cli.Commands { var projectName = Path.GetFileName(project).RemovePostFix(".csproj"); - await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, includePreviews, checkAll: checkAll); + await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, checkAll: checkAll); Logger.LogInformation($"Volo packages are updated in {projectName} project."); return; @@ -141,12 +138,6 @@ namespace Volo.Abp.Cli.Commands public const string Long = "solution-name"; } - public static class IncludePreviews - { - public const string Short = "p"; - public const string Long = "include-previews"; - } - public static class Packages { public const string Npm = "npm"; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs index b125c55648..280d4b8f34 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs @@ -7,22 +7,12 @@ namespace Volo.Abp.Cli.ProjectModification { public class EfCoreMigrationAdder : ITransientDependency { - public void AddMigration(string csprojFile, string module, string startupProject, bool updateDatabase = true) + public void AddMigration(string dbMigrationsCsprojFile, string module, string startupProject) { var moduleName = ParseModuleName(module); var migrationName = "Added_" + moduleName + "_Module" + GetUniquePostFix(); - CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(csprojFile) + "\" && dotnet ef migrations add " + migrationName + GetStartupProjectOption(startupProject)); - - if (updateDatabase) - { - UpdateDatabase(csprojFile, startupProject); - } - } - - protected void UpdateDatabase(string csprojFile, string startupProject) - { - CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(csprojFile) + "\" && dotnet ef database update" + GetStartupProjectOption(startupProject)); + CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigrationsCsprojFile) + "\" && dotnet ef migrations add " + migrationName + GetStartupProjectOption(startupProject)); } protected virtual string ParseModuleName(string fullModuleName) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs index a19af6f52c..1b006c25b6 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Volo.Abp.Cli.Commands.Services; using Volo.Abp.Cli.Http; using Volo.Abp.Cli.ProjectBuilding; +using Volo.Abp.Cli.Utils; using Volo.Abp.DependencyInjection; using Volo.Abp.Json; @@ -214,6 +215,11 @@ namespace Volo.Abp.Cli.ProjectModification { if (string.IsNullOrWhiteSpace(module.EfCoreConfigureMethodName)) { + if (!skipDbMigrations) + { + RunMigrator(projectFiles); + } + return; } @@ -227,6 +233,12 @@ namespace Volo.Abp.Cli.ProjectModification if (dbMigrationsProject == null) { Logger.LogDebug("Solution doesn't have a \".DbMigrations\" project."); + + if (!skipDbMigrations) + { + RunMigrator(projectFiles); + } + return; } @@ -240,9 +252,24 @@ namespace Volo.Abp.Cli.ProjectModification var addedNewBuilder = DbContextFileBuilderConfigureAdder.Add(dbContextFile, module.EfCoreConfigureMethodName); - if (addedNewBuilder && !skipDbMigrations) + if (!skipDbMigrations) + { + if (addedNewBuilder) + { + EfCoreMigrationAdder.AddMigration(dbMigrationsProject, module.Name, startupProject); + } + + RunMigrator(projectFiles); + } + } + + protected virtual async Task RunMigrator(string[] projectFiles) + { + var dbMigratorProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrator.csproj")); + + if (!string.IsNullOrEmpty(dbMigratorProject)) { - EfCoreMigrationAdder.AddMigration(dbMigrationsProject, module.Name, startupProject); + CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigratorProject) + "\" && dotnet run"); } } diff --git a/framework/src/Volo.Abp.Core/System/Linq/PredicateOperator.cs b/framework/src/Volo.Abp.Core/System/Linq/PredicateOperator.cs new file mode 100644 index 0000000000..0732a63110 --- /dev/null +++ b/framework/src/Volo.Abp.Core/System/Linq/PredicateOperator.cs @@ -0,0 +1,269 @@ +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using JetBrains.Annotations; + +namespace System.Linq +{ + // Codes below are taken from https://github.com/scottksmith95/LINQKit project. + + /// The Predicate Operator + public enum PredicateOperator + { + /// The "Or" + Or, + + /// The "And" + And + } + + /// + /// See http://www.albahari.com/expressions for information and examples. + /// + public static class PredicateBuilder + { + private class RebindParameterVisitor : ExpressionVisitor + { + private readonly ParameterExpression _oldParameter; + private readonly ParameterExpression _newParameter; + + public RebindParameterVisitor(ParameterExpression oldParameter, ParameterExpression newParameter) + { + _oldParameter = oldParameter; + _newParameter = newParameter; + } + + protected override Expression VisitParameter(ParameterExpression node) + { + return node == _oldParameter ? _newParameter : base.VisitParameter(node); + } + } + + /// Start an expression + public static ExpressionStarter New(Expression> expr = null) + { + return new ExpressionStarter(expr); + } + + /// Create an expression with a stub expression true or false to use when the expression is not yet started. + public static ExpressionStarter New(bool defaultExpression) + { + return new ExpressionStarter(defaultExpression); + } + + /// OR + public static Expression> Or([NotNull] this Expression> expr1, + [NotNull] Expression> expr2) + { + var expr2Body = new RebindParameterVisitor(expr2.Parameters[0], expr1.Parameters[0]).Visit(expr2.Body); + return Expression.Lambda>(Expression.OrElse(expr1.Body, expr2Body), expr1.Parameters); + } + + /// AND + public static Expression> And([NotNull] this Expression> expr1, + [NotNull] Expression> expr2) + { + var expr2Body = new RebindParameterVisitor(expr2.Parameters[0], expr1.Parameters[0]).Visit(expr2.Body); + return Expression.Lambda>(Expression.AndAlso(expr1.Body, expr2Body), expr1.Parameters); + } + + /// + /// Extends the specified source Predicate with another Predicate and the specified PredicateOperator. + /// + /// The type + /// The source Predicate. + /// The second Predicate. + /// The Operator (can be "And" or "Or"). + /// Expression{Func{T, bool}} + public static Expression> Extend([NotNull] this Expression> first, + [NotNull] Expression> second, PredicateOperator @operator = PredicateOperator.Or) + { + return @operator == PredicateOperator.Or ? first.Or(second) : first.And(second); + } + + /// + /// Extends the specified source Predicate with another Predicate and the specified PredicateOperator. + /// + /// The type + /// The source Predicate. + /// The second Predicate. + /// The Operator (can be "And" or "Or"). + /// Expression{Func{T, bool}} + public static Expression> Extend([NotNull] this ExpressionStarter first, + [NotNull] Expression> second, PredicateOperator @operator = PredicateOperator.Or) + { + return @operator == PredicateOperator.Or ? first.Or(second) : first.And(second); + } + } + + /// + /// ExpressionStarter{T} which eliminates the default 1=0 or 1=1 stub expressions + /// + /// The type + public class ExpressionStarter + { + public ExpressionStarter() : this(false) + { + } + + public ExpressionStarter(bool defaultExpression) + { + if (defaultExpression) + { + DefaultExpression = f => true; + } + else + { + DefaultExpression = f => false; + } + } + + public ExpressionStarter(Expression> exp) : this(false) + { + _predicate = exp; + } + + /// The actual Predicate. It can only be set by calling Start. + private Expression> Predicate => + (IsStarted || !UseDefaultExpression) ? _predicate : DefaultExpression; + + private Expression> _predicate; + + /// Determines if the predicate is started. + public bool IsStarted => _predicate != null; + + /// A default expression to use only when the expression is null + public bool UseDefaultExpression => DefaultExpression != null; + + /// The default expression + public Expression> DefaultExpression { get; set; } + + /// Set the Expression predicate + /// The first expression + public Expression> Start(Expression> exp) + { + if (IsStarted) + { + throw new Exception("Predicate cannot be started again."); + } + + return _predicate = exp; + } + + /// Or + public Expression> Or([NotNull] Expression> expr2) + { + return (IsStarted) ? _predicate = Predicate.Or(expr2) : Start(expr2); + } + + /// And + public Expression> And([NotNull] Expression> expr2) + { + return (IsStarted) ? _predicate = Predicate.And(expr2) : Start(expr2); + } + + /// Show predicate string + public override string ToString() + { + return Predicate?.ToString(); + } + + #region Implicit Operators + + /// + /// Allows this object to be implicitely converted to an Expression{Func{T, bool}}. + /// + /// + public static implicit operator Expression>(ExpressionStarter right) + { + return right?.Predicate; + } + + /// + /// Allows this object to be implicitely converted to an Expression{Func{T, bool}}. + /// + /// + public static implicit operator Func(ExpressionStarter right) + { + return right == null ? null : + (right.IsStarted || right.UseDefaultExpression) ? right.Predicate.Compile() : null; + } + + /// + /// Allows this object to be implicitely converted to an Expression{Func{T, bool}}. + /// + /// + public static implicit operator ExpressionStarter(Expression> right) + { + return right == null ? null : new ExpressionStarter(right); + } + + #endregion + + #region Implement Expression methods and properties + +#if !(NET35) + + /// + public Func Compile() + { + return Predicate.Compile(); + } +#endif + +#if !(NET35 || WINDOWS_APP || NETSTANDARD || PORTABLE || PORTABLE40 || UAP) + /// + public Func Compile(DebugInfoGenerator debugInfoGenerator) { return Predicate.Compile(debugInfoGenerator); } + + /// + public Expression> Update(Expression body, IEnumerable parameters) { return Predicate.Update(body, parameters); } +#endif + + #endregion + + #region Implement LamdaExpression methods and properties + + /// + public Expression Body => Predicate.Body; + + + /// + public ExpressionType NodeType => Predicate.NodeType; + + /// + public ReadOnlyCollection Parameters => Predicate.Parameters; + + /// + public Type Type => Predicate.Type; + +#if !(NET35) + /// + public string Name => Predicate.Name; + + /// + public Type ReturnType => Predicate.ReturnType; + + /// + public bool TailCall => Predicate.TailCall; +#endif + +#if !(NET35 || WINDOWS_APP || NETSTANDARD || PORTABLE || PORTABLE40 || UAP) + /// + public void CompileToMethod(MethodBuilder method) { Predicate.CompileToMethod(method); } + + /// + public void CompileToMethod(MethodBuilder method, DebugInfoGenerator debugInfoGenerator) { Predicate.CompileToMethod(method, debugInfoGenerator); } + +#endif + + #endregion + + #region Implement Expression methods and properties + +#if !(NET35) + /// + public virtual bool CanReduce => Predicate.CanReduce; +#endif + + #endregion + } +} diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJob.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJob.cs index f471fecaa1..a81bfb8c7f 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJob.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJob.cs @@ -1,10 +1,11 @@ +using System; +using System.Threading.Tasks; using Volo.Abp.BackgroundJobs; using Volo.Abp.DependencyInjection; -using Volo.Abp.Threading; namespace Volo.Abp.Emailing { - public class BackgroundEmailSendingJob : BackgroundJob, ITransientDependency + public class BackgroundEmailSendingJob : AsyncBackgroundJob, ITransientDependency { protected IEmailSender EmailSender { get; } @@ -13,9 +14,16 @@ namespace Volo.Abp.Emailing EmailSender = emailSender; } - public override void Execute(BackgroundEmailSendingJobArgs args) + public override async Task ExecuteAsync(BackgroundEmailSendingJobArgs args) { - AsyncHelper.RunSync(() => EmailSender.SendAsync(args.To, args.Subject, args.Body, args.IsBodyHtml)); + if (args.From.IsNullOrWhiteSpace()) + { + await EmailSender.SendAsync(args.To, args.Subject, args.Body, args.IsBodyHtml); + } + else + { + await EmailSender.SendAsync(args.From, args.To, args.Subject, args.Body, args.IsBodyHtml); + } } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJobArgs.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJobArgs.cs index 54da9cddb0..189800e0a8 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJobArgs.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJobArgs.cs @@ -5,12 +5,14 @@ namespace Volo.Abp.Emailing [Serializable] public class BackgroundEmailSendingJobArgs { + public string From { get; set; } + public string To { get; set; } public string Subject { get; set; } public string Body { get; set; } - + /// /// Default: true. /// @@ -18,4 +20,4 @@ namespace Volo.Abp.Emailing //TODO: Add other properties and attachments } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs index f6588de9fb..b278d70d16 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs @@ -69,6 +69,26 @@ namespace Volo.Abp.Emailing ); } + public async Task QueueAsync(string from, string to, string subject, string body, bool isBodyHtml = true) + { + if (!BackgroundJobManager.IsAvailable()) + { + await SendAsync(from, to, subject, body, isBodyHtml); + return; + } + + await BackgroundJobManager.EnqueueAsync( + new BackgroundEmailSendingJobArgs + { + From = from, + To = to, + Subject = subject, + Body = body, + IsBodyHtml = isBodyHtml + } + ); + } + /// /// Should implement this method to send email in derived classes. /// @@ -108,4 +128,4 @@ namespace Volo.Abp.Emailing } } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSender.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSender.cs index 612d8f078c..bdaba4f15b 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSender.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSender.cs @@ -11,12 +11,23 @@ namespace Volo.Abp.Emailing /// /// Sends an email. /// - Task SendAsync(string to, string subject, string body, bool isBodyHtml = true); + Task SendAsync( + string to, + string subject, + string body, + bool isBodyHtml = true + ); /// /// Sends an email. /// - Task SendAsync(string from, string to, string subject, string body, bool isBodyHtml = true); + Task SendAsync( + string from, + string to, + string subject, + string body, + bool isBodyHtml = true + ); /// /// Sends an email. @@ -24,14 +35,33 @@ namespace Volo.Abp.Emailing /// Mail to be sent /// /// Should normalize email? - /// If true, it sets sender address/name if it's not set before and makes mail encoding UTF-8. + /// If true, it sets sender address/name if it's not set before and makes mail encoding UTF-8. /// - Task SendAsync(MailMessage mail, bool normalize = true); + Task SendAsync( + MailMessage mail, + bool normalize = true + ); /// /// Adds an email to queue to send via background jobs. /// - Task QueueAsync(string to, string subject, string body, bool isBodyHtml = true); + Task QueueAsync( + string to, + string subject, + string body, + bool isBodyHtml = true + ); + + /// + /// Adds an email to queue to send via background jobs. + /// + Task QueueAsync( + string from, + string to, + string subject, + string body, + bool isBodyHtml = true + ); //TODO: Add other Queue methods too. Problem: MailMessage is not serializable so can not be used in background jobs. } diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs index f97fd57b18..24ca7d6a34 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs @@ -4,7 +4,6 @@ using System.Net.Mail; using System.Threading.Tasks; using Volo.Abp.BackgroundJobs; using Volo.Abp.DependencyInjection; -using Volo.Abp.Threading; namespace Volo.Abp.Emailing.Smtp { diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionInfoExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionInfoExtensions.cs index 91184ecdeb..17b495cdad 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionInfoExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionInfoExtensions.cs @@ -6,6 +6,7 @@ namespace Volo.Abp.ObjectExtending { public static class EfCoreObjectExtensionInfoExtensions { + [Obsolete("Use MapEfCoreProperty with EntityTypeAndPropertyBuildAction parameters.")] public static ObjectExtensionInfo MapEfCoreProperty( [NotNull] this ObjectExtensionInfo objectExtensionInfo, [NotNull] string propertyName, @@ -18,6 +19,7 @@ namespace Volo.Abp.ObjectExtending ); } + [Obsolete("Use MapEfCoreProperty with EntityTypeAndPropertyBuildAction parameters.")] public static ObjectExtensionInfo MapEfCoreProperty( [NotNull] this ObjectExtensionInfo objectExtensionInfo, [NotNull] Type propertyType, diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionManagerExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionManagerExtensions.cs index cf6d0e1368..14fcc93784 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionManagerExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionManagerExtensions.cs @@ -39,6 +39,7 @@ namespace Volo.Abp.ObjectExtending ); } + [Obsolete("Use MapEfCoreProperty with EntityTypeAndPropertyBuildAction parameters.")] public static ObjectExtensionManager MapEfCoreProperty( [NotNull] this ObjectExtensionManager objectExtensionManager, [NotNull] string propertyName, @@ -53,6 +54,7 @@ namespace Volo.Abp.ObjectExtending ); } + [Obsolete("Use MapEfCoreProperty with EntityTypeAndPropertyBuildAction parameters.")] public static ObjectExtensionManager MapEfCoreProperty( [NotNull] this ObjectExtensionManager objectExtensionManager, [NotNull] Type entityType, diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionPropertyInfoExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionPropertyInfoExtensions.cs index 9fb466c4cd..f4dae81fac 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionPropertyInfoExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionPropertyInfoExtensions.cs @@ -23,6 +23,7 @@ namespace Volo.Abp.ObjectExtending return propertyExtension; } + [Obsolete("Use MapEfCore with EntityTypeAndPropertyBuildAction parameters.")] [NotNull] public static ObjectExtensionPropertyInfo MapEfCore( [NotNull] this ObjectExtensionPropertyInfo propertyExtension, @@ -49,7 +50,7 @@ namespace Volo.Abp.ObjectExtending propertyExtension.Configuration[EfCorePropertyConfigurationName] = new ObjectExtensionPropertyInfoEfCoreMappingOptions( propertyExtension, - entityTypeAndPropertyBuildAction: entityTypeAndPropertyBuildAction + entityTypeAndPropertyBuildAction ); return propertyExtension; diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoEfCoreMappingOptions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoEfCoreMappingOptions.cs index 15a818478b..7bf9c313e3 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoEfCoreMappingOptions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoEfCoreMappingOptions.cs @@ -12,12 +12,14 @@ namespace Volo.Abp.ObjectExtending [NotNull] public ObjectExtensionInfo ObjectExtension => ExtensionProperty.ObjectExtension; + [Obsolete("Use EntityTypeAndPropertyBuildAction property.")] [CanBeNull] public Action PropertyBuildAction { get; set; } [CanBeNull] public Action EntityTypeAndPropertyBuildAction { get; set; } + [Obsolete("Use other constructors.")] public ObjectExtensionPropertyInfoEfCoreMappingOptions( [NotNull] ObjectExtensionPropertyInfo extensionProperty, [CanBeNull] Action propertyBuildAction = null, @@ -28,5 +30,20 @@ namespace Volo.Abp.ObjectExtending PropertyBuildAction = propertyBuildAction; EntityTypeAndPropertyBuildAction = entityTypeAndPropertyBuildAction; } + + public ObjectExtensionPropertyInfoEfCoreMappingOptions( + [NotNull] ObjectExtensionPropertyInfo extensionProperty) + { + ExtensionProperty = Check.NotNull(extensionProperty, nameof(extensionProperty)); + } + + public ObjectExtensionPropertyInfoEfCoreMappingOptions( + [NotNull] ObjectExtensionPropertyInfo extensionProperty, + [CanBeNull] Action entityTypeAndPropertyBuildAction) + { + ExtensionProperty = Check.NotNull(extensionProperty, nameof(extensionProperty)); + + EntityTypeAndPropertyBuildAction = entityTypeAndPropertyBuildAction; + } } } diff --git a/framework/src/Volo.Abp.GlobalFeatures/FodyWeavers.xml b/framework/src/Volo.Abp.GlobalFeatures/FodyWeavers.xml new file mode 100644 index 0000000000..bc5a74a236 --- /dev/null +++ b/framework/src/Volo.Abp.GlobalFeatures/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + diff --git a/framework/src/Volo.Abp.GlobalFeatures/FodyWeavers.xsd b/framework/src/Volo.Abp.GlobalFeatures/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.GlobalFeatures/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.GlobalFeatures/Properties/AssemblyInfo.cs b/framework/src/Volo.Abp.GlobalFeatures/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..e4ff8a95bd --- /dev/null +++ b/framework/src/Volo.Abp.GlobalFeatures/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly:InternalsVisibleTo("Volo.Abp.GlobalFeatures.Tests")] diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo.Abp.GlobalFeatures.csproj b/framework/src/Volo.Abp.GlobalFeatures/Volo.Abp.GlobalFeatures.csproj new file mode 100644 index 0000000000..f4afe8f9f7 --- /dev/null +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo.Abp.GlobalFeatures.csproj @@ -0,0 +1,21 @@ + + + + + + + netstandard2.0 + Volo.Abp.GlobalFeatures + Volo.Abp.GlobalFeatures + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/AbpGlobalFeaturesModule.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/AbpGlobalFeaturesModule.cs new file mode 100644 index 0000000000..d537e9f8fd --- /dev/null +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/AbpGlobalFeaturesModule.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.GlobalFeatures +{ + public class AbpGlobalFeaturesModule : AbpModule + { + + } +} diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeature.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeature.cs new file mode 100644 index 0000000000..c7fc9304a8 --- /dev/null +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeature.cs @@ -0,0 +1,51 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.GlobalFeatures +{ + public abstract class GlobalFeature + { + [NotNull] + public GlobalModuleFeatures Module { get; } + + [NotNull] + public GlobalFeatureManager FeatureManager { get; } + + [NotNull] + public string FeatureName { get; } + + public bool IsEnabled + { + get => FeatureManager.IsEnabled(FeatureName); + set => SetEnabled(value); + } + + protected GlobalFeature([NotNull] GlobalModuleFeatures module) + { + Module = Check.NotNull(module, nameof(module)); + FeatureManager = Module.FeatureManager; + FeatureName = GlobalFeatureNameAttribute.GetName(GetType()); + } + + public virtual void Enable() + { + FeatureManager.Enable(FeatureName); + } + + public virtual void Disable() + { + FeatureManager.Disable(FeatureName); + } + + public void SetEnabled(bool isEnabled) + { + if (isEnabled) + { + Enable(); + } + else + { + Disable(); + } + } + } +} diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureDictionary.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureDictionary.cs new file mode 100644 index 0000000000..a81632f8d5 --- /dev/null +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureDictionary.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Volo.Abp.GlobalFeatures +{ + public class GlobalFeatureDictionary : Dictionary + { + + } +} diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureManager.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureManager.cs new file mode 100644 index 0000000000..19a48a9da3 --- /dev/null +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureManager.cs @@ -0,0 +1,59 @@ +using System; + using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.GlobalFeatures +{ + public class GlobalFeatureManager + { + public static GlobalFeatureManager Instance { get; protected set; } = new GlobalFeatureManager(); + + /// + /// A common dictionary to store arbitrary configurations. + /// + [NotNull] + public Dictionary Configuration { get; } + + public GlobalModuleFeaturesDictionary Modules { get; } + + protected HashSet EnabledFeatures { get; } + + internal GlobalFeatureManager() + { + EnabledFeatures = new HashSet(); + Configuration = new Dictionary(); + Modules = new GlobalModuleFeaturesDictionary(this); + } + + public virtual bool IsEnabled() + where TFeature : GlobalFeature + { + return IsEnabled(GlobalFeatureNameAttribute.GetName()); + } + + public virtual bool IsEnabled([NotNull] Type featureType) + { + return IsEnabled(GlobalFeatureNameAttribute.GetName(featureType)); + } + + public virtual bool IsEnabled(string featureName) + { + return EnabledFeatures.Contains(featureName); + } + + protected internal void Enable(string featureName) + { + EnabledFeatures.AddIfNotContains(featureName); + } + + protected internal void Disable(string featureName) + { + EnabledFeatures.Remove(featureName); + } + + public virtual IEnumerable GetEnabledFeatureNames() + { + return EnabledFeatures; + } + } +} diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureNameAttribute.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureNameAttribute.cs new file mode 100644 index 0000000000..2b987f6cb1 --- /dev/null +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureNameAttribute.cs @@ -0,0 +1,44 @@ +using System; +using System.Linq; +using System.Reflection; +using JetBrains.Annotations; + +namespace Volo.Abp.GlobalFeatures +{ + [AttributeUsage(AttributeTargets.Class)] + public class GlobalFeatureNameAttribute : Attribute + { + [NotNull] + public string Name { get; } + + public GlobalFeatureNameAttribute([NotNull] string name) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name)); + } + + public static string GetName() + where TFeature : GlobalFeature + { + return GetName(typeof(TFeature)); + } + + [NotNull] + public static string GetName([NotNull] Type type) + { + Check.NotNull(type, nameof(type)); + + var attribute = type + .GetCustomAttributes() + .FirstOrDefault(); + + if (attribute == null) + { + throw new AbpException($"{type.AssemblyQualifiedName} should define the {typeof(GlobalFeatureNameAttribute).FullName} atttribute!"); + } + + return attribute + .As() + .Name; + } + } +} diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalModuleFeatures.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalModuleFeatures.cs new file mode 100644 index 0000000000..7d1b844d45 --- /dev/null +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalModuleFeatures.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using JetBrains.Annotations; + +namespace Volo.Abp.GlobalFeatures +{ + public abstract class GlobalModuleFeatures + { + [NotNull] + public GlobalFeatureManager FeatureManager { get; } + + [NotNull] + protected GlobalFeatureDictionary AllFeatures { get; } + + protected GlobalModuleFeatures( + [NotNull] GlobalFeatureManager featureManager) + { + FeatureManager = Check.NotNull(featureManager, nameof(featureManager)); + AllFeatures = new GlobalFeatureDictionary(); + } + + public virtual void Enable() + where TFeature : GlobalFeature + { + GetFeature().Enable(); + } + + public virtual void Disable() + where TFeature : GlobalFeature + { + GetFeature().Disable(); + } + + public virtual void SetEnabled(bool isEnabled) + where TFeature : GlobalFeature + { + GetFeature().SetEnabled(isEnabled); + } + + public virtual void Enable(string featureName) + { + GetFeature(featureName).Enable(); + } + + public virtual void Disable(string featureName) + { + GetFeature(featureName).Disable(); + } + + public virtual void SetEnabled(string featureName, bool isEnabled) + { + GetFeature(featureName).SetEnabled(isEnabled); + } + + public virtual void EnableAll() + { + foreach (var feature in AllFeatures.Values) + { + feature.Enable(); + } + } + + public virtual void DisableAll() + { + foreach (var feature in AllFeatures.Values) + { + feature.Disable(); + } + } + + public virtual GlobalFeature GetFeature(string featureName) + { + var feature = AllFeatures.GetOrDefault(featureName); + if (feature == null) + { + throw new AbpException($"There is no feature defined by name '{featureName}'."); + } + + return feature; + } + + public virtual TFeature GetFeature() + where TFeature : GlobalFeature + { + return (TFeature) GetFeature(GlobalFeatureNameAttribute.GetName()); + } + + public virtual IReadOnlyList GetFeatures() + { + return AllFeatures.Values.ToImmutableList(); + } + + protected void AddFeature(GlobalFeature feature) + { + AllFeatures[feature.FeatureName] = feature; + } + } +} diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalModuleFeaturesDictionary.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalModuleFeaturesDictionary.cs new file mode 100644 index 0000000000..b89bf2fe36 --- /dev/null +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalModuleFeaturesDictionary.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.GlobalFeatures +{ + public class GlobalModuleFeaturesDictionary : Dictionary + { + public GlobalFeatureManager FeatureManager { get; } + + public GlobalModuleFeaturesDictionary( + [NotNull] GlobalFeatureManager featureManager) + { + FeatureManager = Check.NotNull(featureManager, nameof(featureManager)); + } + } +} diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/RequiresGlobalFeatureAttribute.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/RequiresGlobalFeatureAttribute.cs new file mode 100644 index 0000000000..69df268e97 --- /dev/null +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/RequiresGlobalFeatureAttribute.cs @@ -0,0 +1,28 @@ +using System; +using JetBrains.Annotations; + +namespace Volo.Abp.GlobalFeatures +{ + [AttributeUsage(AttributeTargets.Class)] + public class RequiresGlobalFeatureAttribute : Attribute + { + public Type Type { get; } + + public string Name { get; } + + public RequiresGlobalFeatureAttribute([NotNull] Type type) + { + Type = Check.NotNull(type, nameof(type)); + } + + public RequiresGlobalFeatureAttribute([NotNull] string name) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name)); + } + + public virtual string GetFeatureName() + { + return Name ?? GlobalFeatureNameAttribute.GetName(Type); + } + } +} diff --git a/framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/tr.json b/framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/tr.json index 7d04b9aa50..e6c90ea87c 100644 --- a/framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/tr.json +++ b/framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/tr.json @@ -13,7 +13,7 @@ "DisplayName:Abp.Ldap.UserName": "Kullanıcı adı", "Description:Abp.Ldap.UserName": "Kullanıcı adı", - "DisplayName:Abp.Ldap.Password": "parola", - "Description:Abp.Ldap.Password": "parola" + "DisplayName:Abp.Ldap.Password": "Parola", + "Description:Abp.Ldap.Password": "Parola" } } diff --git a/framework/src/Volo.Abp.MailKit/Volo/Abp/MailKit/MailKitSmtpEmailSender.cs b/framework/src/Volo.Abp.MailKit/Volo/Abp/MailKit/MailKitSmtpEmailSender.cs index a3cb4ea499..0fb93e8f40 100644 --- a/framework/src/Volo.Abp.MailKit/Volo/Abp/MailKit/MailKitSmtpEmailSender.cs +++ b/framework/src/Volo.Abp.MailKit/Volo/Abp/MailKit/MailKitSmtpEmailSender.cs @@ -56,21 +56,21 @@ namespace Volo.Abp.MailKit protected virtual async Task ConfigureClient(SmtpClient client) { - client.Connect( + await client.ConnectAsync( await SmtpConfiguration.GetHostAsync(), await SmtpConfiguration.GetPortAsync(), await GetSecureSocketOption() - ); + ); if (await SmtpConfiguration.GetUseDefaultCredentialsAsync()) { return; } - client.Authenticate( + await client.AuthenticateAsync( await SmtpConfiguration.GetUserNameAsync(), await SmtpConfiguration.GetPasswordAsync() - ); + ); } protected virtual async Task GetSecureSocketOption() diff --git a/framework/src/Volo.Abp.Quartz/Volo.Abp.Quartz.csproj b/framework/src/Volo.Abp.Quartz/Volo.Abp.Quartz.csproj index 8e29b1f803..00da6b31b6 100644 --- a/framework/src/Volo.Abp.Quartz/Volo.Abp.Quartz.csproj +++ b/framework/src/Volo.Abp.Quartz/Volo.Abp.Quartz.csproj @@ -15,7 +15,9 @@ - + + + diff --git a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzJobFactory.cs b/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzJobFactory.cs deleted file mode 100644 index 428dbe03af..0000000000 --- a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzJobFactory.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Concurrent; -using Microsoft.Extensions.DependencyInjection; -using Quartz; -using Quartz.Spi; - -namespace Volo.Abp.Quartz -{ - /// - /// Get the job from the dependency injection - /// - public class AbpQuartzJobFactory : IJobFactory - { - private readonly IServiceProvider _serviceProvider; - - private readonly ConcurrentDictionary _scopes = new ConcurrentDictionary(); - - public AbpQuartzJobFactory(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) - { - var scope = _serviceProvider.CreateScope(); - var job = scope.ServiceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob; - if (job == null) - { - throw new ArgumentException("Given job does not implement IJob"); - } - _scopes.TryAdd(job, scope); - return job; - } - - public void ReturnJob(IJob job) - { - _scopes.TryRemove(job, out var serviceScope); - serviceScope?.Dispose(); - } - } -} diff --git a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs b/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs index 3a2413a9b8..2429f617d9 100644 --- a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs +++ b/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.Options; using Quartz; using Quartz.Impl; -using Quartz.Spi; +using Quartz.Impl.AdoJobStore.Common; using Volo.Abp.Modularity; using Volo.Abp.Threading; @@ -15,8 +15,48 @@ namespace Volo.Abp.Quartz public override void ConfigureServices(ServiceConfigurationContext context) { var options = context.Services.ExecutePreConfiguredActions(); - context.Services.AddSingleton(AsyncHelper.RunSync(() => new StdSchedulerFactory(options.Properties).GetScheduler())); - context.Services.AddSingleton(typeof(IJobFactory), typeof(AbpQuartzJobFactory)); + + // TODO: Remove this once Pomelo update MySqlConnector to >= 1.0.0 : https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/pull/1103 + AdaptMysqlConnector(); + + context.Services.AddQuartz(options.Properties, build => + { + // these are the defaults + if (options.Properties[StdSchedulerFactory.PropertySchedulerJobFactoryType] == null) + { + build.UseMicrosoftDependencyInjectionScopedJobFactory(); + } + + if (options.Properties[StdSchedulerFactory.PropertySchedulerTypeLoadHelperType] == null) + { + build.UseSimpleTypeLoader(); + } + + if (options.Properties[StdSchedulerFactory.PropertyJobStoreType] == null) + { + build.UseInMemoryStore(); + } + + if (options.Properties[StdSchedulerFactory.PropertyThreadPoolType] == null) + { + build.UseDefaultThreadPool(tp => + { + tp.MaxConcurrency = 10; + }); + } + + if (options.Properties["quartz.plugin.timeZoneConverter.type"] == null) + { + build.UseTimeZoneConverter(); + } + + options.Configurator?.Invoke(build); + }); + + context.Services.AddSingleton(serviceProvider => + { + return AsyncHelper.RunSync(() => serviceProvider.GetService().GetScheduler()); + }); Configure(quartzOptions => { @@ -30,7 +70,6 @@ namespace Volo.Abp.Quartz var options = context.ServiceProvider.GetRequiredService>().Value; _scheduler = context.ServiceProvider.GetService(); - _scheduler.JobFactory = context.ServiceProvider.GetService(); AsyncHelper.RunSync(() => options.StartSchedulerFactory.Invoke(_scheduler)); } @@ -42,5 +81,28 @@ namespace Volo.Abp.Quartz AsyncHelper.RunSync(() => _scheduler.Shutdown()); } } + + private void AdaptMysqlConnector() + { + var mySqlAvailable = System.Type.GetType("MySql.Data.MySqlClient.MySqlConnection, MySqlConnector") != null; + if (mySqlAvailable) + { + // Overriding the default 'MySqlConnector' provider to use the old 'MySql.Data' namespace found in MySqlConnector < 1.0.0 + DbProvider.RegisterDbMetadata("MySqlConnector", new DbMetadata() + { + ProductName = "MySQL, MySqlConnector provider", + AssemblyName = "MySqlConnector", + ConnectionType = System.Type.GetType("MySql.Data.MySqlClient.MySqlConnection, MySqlConnector"), + CommandType = System.Type.GetType("MySql.Data.MySqlClient.MySqlCommand, MySqlConnector"), + ParameterType = System.Type.GetType("MySql.Data.MySqlClient.MySqlParameter, MySqlConnector"), + ParameterDbType = System.Type.GetType("MySql.Data.MySqlClient.MySqlDbType, MySqlConnector"), + ParameterDbTypePropertyName = "MySqlDbType", + ParameterNamePrefix = "?", + ExceptionType = System.Type.GetType("MySql.Data.MySqlClient.MySqlException, MySqlConnector"), + BindByName = true, + DbBinaryTypeName = "Blob" + }); + } + } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzOptions.cs b/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzOptions.cs index 9998676042..7dbf415cac 100644 --- a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzOptions.cs +++ b/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzOptions.cs @@ -13,6 +13,8 @@ namespace Volo.Abp.Quartz /// public NameValueCollection Properties { get; set; } + public Action Configurator { get; set; } + /// /// How long Quartz should wait before starting. Default: 0. /// @@ -45,4 +47,4 @@ namespace Volo.Abp.Quartz } } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj index b3842f7271..c6450e6df1 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj @@ -61,6 +61,14 @@ true PreserveNewest + + true + PreserveNewest + + + true + PreserveNewest + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs index d7544ebf0d..c666aeaa44 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs @@ -1,19 +1,23 @@ using System; +using System.Collections.Generic; using System.Security.Claims; using Localization.Resources.AbpUi; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.Authorization; +using Volo.Abp.AspNetCore.Mvc.GlobalFeatures; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.Localization.Resource; using Volo.Abp.AspNetCore.Security.Claims; using Volo.Abp.AspNetCore.TestBase; using Volo.Abp.Autofac; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Localization; using Volo.Abp.MemoryDb; using Volo.Abp.Modularity; using Volo.Abp.TestApp; +using Volo.Abp.Threading; using Volo.Abp.Validation.Localization; using Volo.Abp.VirtualFileSystem; @@ -27,6 +31,8 @@ namespace Volo.Abp.AspNetCore.Mvc )] public class AbpAspNetCoreMvcTestModule : AbpModule { + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.PreConfigure(options => @@ -40,6 +46,13 @@ namespace Volo.Abp.AspNetCore.Mvc public override void ConfigureServices(ServiceConfigurationContext context) { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.GetOrAdd(AbpAspNetCoreMvcTestFeatures.ModuleName, + () => new AbpAspNetCoreMvcTestFeatures(GlobalFeatureManager.Instance)) + .EnableAll(); + }); + context.Services.AddAuthorization(options => { options.AddPolicy("MyClaimTestPolicy", policy => diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/AbpAspNetCoreMvcTestFeatures.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/AbpAspNetCoreMvcTestFeatures.cs new file mode 100644 index 0000000000..e95380e461 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/AbpAspNetCoreMvcTestFeatures.cs @@ -0,0 +1,30 @@ +using JetBrains.Annotations; +using Volo.Abp.GlobalFeatures; + +namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures +{ + [GlobalFeatureName(Name)] + public class AbpAspNetCoreMvcTestFeature1 : Abp.GlobalFeatures.GlobalFeature + { + public const string Name = "AbpAspNetCoreMvcTest.Feature1"; + + internal AbpAspNetCoreMvcTestFeature1([NotNull] AbpAspNetCoreMvcTestFeatures abpAspNetCoreMvcTestFeatures) + : base(abpAspNetCoreMvcTestFeatures) + { + + } + } + + public class AbpAspNetCoreMvcTestFeatures : GlobalModuleFeatures + { + public const string ModuleName = "AbpAspNetCoreMvcTest"; + + public AbpAspNetCoreMvcTestFeature1 Reactions => GetFeature(); + + public AbpAspNetCoreMvcTestFeatures([NotNull] GlobalFeatureManager featureManager) + : base(featureManager) + { + AddFeature(new AbpAspNetCoreMvcTestFeature1(this)); + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/DisabledGlobalFeatureTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/DisabledGlobalFeatureTestController.cs new file mode 100644 index 0000000000..e8197f4374 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/DisabledGlobalFeatureTestController.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.GlobalFeatures; + +namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures +{ + [RequiresGlobalFeature("Not-Enabled-Feature")] + [Route("api/DisabledGlobalFeatureTestController-Test")] + public class DisabledGlobalFeatureTestController : AbpController + { + [HttpGet] + [Route("TestMethod")] + public string TestMethod() + { + return "TestMethod"; + } + } + +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/DisabledGlobalFeatureTestPage.cshtml b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/DisabledGlobalFeatureTestPage.cshtml new file mode 100644 index 0000000000..17e1e154f5 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/DisabledGlobalFeatureTestPage.cshtml @@ -0,0 +1,19 @@ +@page +@model Volo.Abp.AspNetCore.Mvc.GlobalFeatures.DisabledGlobalFeatureTestPage + +@{ + Layout = null; +} + + + + + + + + +
+ +
+ + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/DisabledGlobalFeatureTestPage.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/DisabledGlobalFeatureTestPage.cshtml.cs new file mode 100644 index 0000000000..13ef0dddb5 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/DisabledGlobalFeatureTestPage.cshtml.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc.RazorPages; +using Volo.Abp.GlobalFeatures; + +namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures +{ + [RequiresGlobalFeature("Not-Enabled-Feature")] + public class DisabledGlobalFeatureTestPage : PageModel + { + public void OnGet() + { + + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/EnabledGlobalFeatureTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/EnabledGlobalFeatureTestController.cs new file mode 100644 index 0000000000..9cc1d4700a --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/EnabledGlobalFeatureTestController.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.GlobalFeatures; + +namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures +{ + [RequiresGlobalFeature(AbpAspNetCoreMvcTestFeature1.Name)] + [Route("api/EnabledGlobalFeatureTestController-Test")] + public class EnabledGlobalFeatureTestController : AbpController + { + [HttpGet] + [Route("TestMethod")] + public string TestMethod() + { + return "TestMethod"; + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/EnabledGlobalFeatureTestPage.cshtml b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/EnabledGlobalFeatureTestPage.cshtml new file mode 100644 index 0000000000..56e8934446 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/EnabledGlobalFeatureTestPage.cshtml @@ -0,0 +1,19 @@ +@page +@model Volo.Abp.AspNetCore.Mvc.GlobalFeatures.EnabledGlobalFeatureTestPage + +@{ + Layout = null; +} + + + + + + + + +
+ +
+ + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/EnabledGlobalFeatureTestPage.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/EnabledGlobalFeatureTestPage.cshtml.cs new file mode 100644 index 0000000000..bc98d06344 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/EnabledGlobalFeatureTestPage.cshtml.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc.RazorPages; +using Volo.Abp.GlobalFeatures; + +namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures +{ + [RequiresGlobalFeature(AbpAspNetCoreMvcTestFeature1.Name)] + public class EnabledGlobalFeatureTestPage : PageModel + { + public void OnGet() + { + + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/RequiresGlobalFeatureTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/RequiresGlobalFeatureTestController_Tests.cs new file mode 100644 index 0000000000..c01d9bd508 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/RequiresGlobalFeatureTestController_Tests.cs @@ -0,0 +1,24 @@ +using System.Net; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures +{ + public class RequiresGlobalFeatureTestController_Tests: AspNetCoreMvcTestBase + { + [Fact] + public async Task Should_404_If_Feature_Disabled() + { + var result = await GetResponseAsync("/api/DisabledGlobalFeatureTestController-Test/TestMethod", HttpStatusCode.NotFound); + result.StatusCode.ShouldBe(HttpStatusCode.NotFound); + } + + [Fact] + public async Task Should_Pass_Check_If_Feature_Enabled() + { + var result = await GetResponseAsync("/api/EnabledGlobalFeatureTestController-Test/TestMethod", HttpStatusCode.OK); + result.StatusCode.ShouldBe(HttpStatusCode.OK); + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/RequiresGlobalFeatureTestPage_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/RequiresGlobalFeatureTestPage_Tests.cs new file mode 100644 index 0000000000..634eefe1fa --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/RequiresGlobalFeatureTestPage_Tests.cs @@ -0,0 +1,24 @@ +using System.Net; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures +{ + public class RequiresGlobalFeatureTestPage_Tests : AspNetCoreMvcTestBase + { + [Fact] + public async Task Should_404_If_Feature_Disabled() + { + var result = await GetResponseAsync("/GlobalFeatures/DisabledGlobalFeatureTestPage", HttpStatusCode.NotFound); + result.StatusCode.ShouldBe(HttpStatusCode.NotFound); + } + + [Fact] + public async Task Should_Pass_Check_If_Feature_Enabled() + { + var result = await GetResponseAsync("/GlobalFeatures/EnabledGlobalFeatureTestPage", HttpStatusCode.OK); + result.StatusCode.ShouldBe(HttpStatusCode.OK); + } + } +} diff --git a/framework/test/Volo.Abp.Core.Tests/System/Linq/PredicateBuilder_Tests.cs b/framework/test/Volo.Abp.Core.Tests/System/Linq/PredicateBuilder_Tests.cs new file mode 100644 index 0000000000..d28617bc27 --- /dev/null +++ b/framework/test/Volo.Abp.Core.Tests/System/Linq/PredicateBuilder_Tests.cs @@ -0,0 +1,72 @@ +using Shouldly; +using Xunit; + +namespace System.Linq +{ + public class PredicateBuilder_Tests + { + [Fact] + public void Test1() + { + var args = new TestArgs(); + var predicate = PredicateBuilder.New(); + + predicate = predicate.And(t => args.Value == t.Value); + + var func = predicate.Compile(); + + args.Value = true; + var r2 = func(new TestObj { Value = true }); + r2.ShouldBeTrue(); + + args.Value = false; + var r1 = func(new TestObj { Value = false }); + r1.ShouldBeTrue(); + + args = new TestArgs {Value = true}; + var r3 = func(new TestObj { Value = false }); + r3.ShouldBeFalse(); + + args = new TestArgs { Value = false }; + var r4 = func(new TestObj { Value = false }); + r4.ShouldBeTrue(); + } + + [Fact] + public void Test2() + { + var args = new TestArgs(); + var predicate = PredicateBuilder.New(); + + predicate = predicate.And(t => !args.Value); + + var func = predicate.Compile(); + + args.Value = true; + var r2 = func(new TestObj { Value = true }); + r2.ShouldBeFalse(); + + args.Value = false; + var r1 = func(new TestObj { Value = false }); + r1.ShouldBeTrue(); + + args = new TestArgs { Value = true }; + var r3 = func(new TestObj { Value = false }); + r3.ShouldBeFalse(); + + args = new TestArgs { Value = false }; + var r4 = func(new TestObj { Value = false }); + r4.ShouldBeTrue(); + } + + public class TestArgs + { + public bool Value { get; set; } + } + + public class TestObj + { + public bool Value { get; set; } + } + } +} diff --git a/framework/test/Volo.Abp.GlobalFeatures.Tests/Volo.Abp.GlobalFeatures.Tests.csproj b/framework/test/Volo.Abp.GlobalFeatures.Tests/Volo.Abp.GlobalFeatures.Tests.csproj new file mode 100644 index 0000000000..c0a973f8a5 --- /dev/null +++ b/framework/test/Volo.Abp.GlobalFeatures.Tests/Volo.Abp.GlobalFeatures.Tests.csproj @@ -0,0 +1,16 @@ + + + + + + netcoreapp3.1 + + + + + + + + + + diff --git a/framework/test/Volo.Abp.GlobalFeatures.Tests/Volo/Abp/GlobalFeatures/GlobalFeatureManager_Tests.cs b/framework/test/Volo.Abp.GlobalFeatures.Tests/Volo/Abp/GlobalFeatures/GlobalFeatureManager_Tests.cs new file mode 100644 index 0000000000..6d8e3a9d25 --- /dev/null +++ b/framework/test/Volo.Abp.GlobalFeatures.Tests/Volo/Abp/GlobalFeatures/GlobalFeatureManager_Tests.cs @@ -0,0 +1,23 @@ +using Shouldly; +using Xunit; + +namespace Volo.Abp.GlobalFeatures +{ + public class GlobalFeatureManager_Tests + { + private readonly GlobalFeatureManager _featureManeger; + + public GlobalFeatureManager_Tests() + { + _featureManeger = new GlobalFeatureManager(); + } + + [Fact] + public void Enable_Feature_By_Name() + { + _featureManeger.IsEnabled("Feature1").ShouldBeFalse(); + _featureManeger.Enable("Feature1"); + _featureManeger.IsEnabled("Feature1").ShouldBeTrue(); + } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/IAccountAppService.cs b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/IAccountAppService.cs index 74d6bb3f7a..4365969dc6 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/IAccountAppService.cs +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/IAccountAppService.cs @@ -7,5 +7,9 @@ namespace Volo.Abp.Account public interface IAccountAppService : IApplicationService { Task RegisterAsync(RegisterDto input); + + Task SendPasswordResetCodeAsync(SendPasswordResetCodeDto input); + + Task ResetPasswordAsync(ResetPasswordDto input); } -} \ No newline at end of file +} diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json index 2940fff63b..e9215d3f5a 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json @@ -44,6 +44,15 @@ "LoggedOutTitle": "Signed Out", "LoggedOutText": "You have been signed out and you will be redirected soon.", "ReturnToText": "Click here to redirect to {0}", - "OrLoginWith": "Or login with;" + "OrLoginWith": "Or login with;", + "ForgotPassword": "Forgot password?", + "SendPasswordResetLink_Information": "A password reset link will be sent to your email to reset your password. If you don't get an email within a few minutes, please re-try.", + "PasswordResetMailSentMessage": "Account recovery email sent to your e-mail address. If you don't see this email in your inbox within 15 minutes, look for it in your junk mail folder. If you find it there, please mark it as -Not Junk-. ", + "ResetPassword": "Reset Password", + "ConfirmPassword": "Confirm (repeat) the password", + "ResetPassword_Information": "Please enter your new password.", + "YourPasswordIsSuccessfullyReset": "Your password is successfully reset.", + "GoToTheApplication": "Go to the application", + "BackToLogin": "Back to login" } } diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json index d95e591f8a..747cc752c7 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/sl.json @@ -40,6 +40,14 @@ "DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Je lastna registracija uporabnika omogočena", "Description:Abp.Account.IsSelfRegistrationEnabled": "Ali lahko uporabnik sam registrira račun.", "DisplayName:Abp.Account.EnableLocalLogin": "Avtenticirajte se z lokalnim računom", - "Description:Abp.Account.EnableLocalLogin": "Označuje, ali bo strežnik uporabnikom omogočil avtentikacijo z lokalnim računom." + "Description:Abp.Account.EnableLocalLogin": "Označuje, ali bo strežnik uporabnikom omogočil avtentikacijo z lokalnim računom.", + "ForgotPassword": "Ste pozabili geslo?", + "SendPasswordResetLink_Information": "Na vaš e-poštni naslov bo poslana povezava za ponastavitev gesla za ponastavitev gesla. Če v nekaj minutah ne dobite e-poštnega sporočila, poskusite znova.", + "PasswordResetMailSentMessage": "E-poštno sporočilo za obnovitev računa je bilo poslano na vaš e-poštni naslov. Če v 15 minutah tega e-poštnega sporočila ne vidite v mapi »Prejeto«, ga poiščite v mapi z neželeno pošto. Če ga najdete tam, ga prosimo označite kot -Ni neželeno-. ", + "ResetPassword": "Ponastavitev gesla", + "ConfirmPassword": "Potrditev (ponovitev) gesla", + "ResetPassword_Information": "Prosimo vnesite vaše novo geslo.", + "YourPasswordIsSuccessfullyReset": "Vaše geslo je uspešno ponastavljeno.", + "BackToLogin": "Nazaj na prijavo" } } diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json index 3938a34984..bbb2ac3638 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json @@ -43,6 +43,15 @@ "Description:Abp.Account.EnableLocalLogin": "Sunucunun, kullanıcıların yerel bir hesapla kimlik doğrulamasına izin verip vermeyeceğini belirtir.", "LoggedOutTitle": "Çıkış Yaptınız", "LoggedOutText": "Çıkış yaptınız ve birazdan yönlendirileceksiniz.", - "ReturnToText": "{0} uygulamasına dönmek için tıklayın." + "ReturnToText": "{0} uygulamasına dönmek için tıklayın.", + "ForgotPassword": "Şifremi unuttum", + "SendPasswordResetLink_Information": "E-posta adresinize bir şifre sıfırlama bağlantısı gönderilecektir. Birkaç dakika içerisinde bir e-posta almazsanız lütfen tekrar deneyin.", + "PasswordResetMailSentMessage": "E-posta adresinize bir şifre sıfırlama bağlantısı gönderilmiştir. Lütfen e-posta adresinizi kontrol ediniz. Eğer 15 dakika içinde, bu e-postayı gelen kutusunda bulamazsanız, gereksiz veya istenmeyen e-posta kutularına bakınız.", + "ResetPassword": "Şifre Yenileme", + "ConfirmPassword": "Şifre (tekrar)", + "ResetPassword_Information": "Lütfen yeni şifrenizi belirleyin.", + "YourPasswordIsSuccessfullyReset": "Şifreniz başarıyla sıfırlandı.", + "GoToTheApplication": "Uygulamaya git", + "BackToLogin": "Girişe dön" } } diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json index 49a4604372..a7be7fd0b8 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json @@ -43,6 +43,15 @@ "Description:Abp.Account.EnableLocalLogin": "服务器是否将允许用户使用本地帐户进行身份验证。", "LoggedOutTitle": "注销", "LoggedOutText": "你已成功注销并将马上返回.", - "ReturnToText": "点击此处返回到 {0}" + "ReturnToText": "点击此处返回到 {0}", + "ForgotPassword": "忘记密码?", + "SendPasswordResetLink_Information": "密码重置链接将发送到您的电子邮件以重置密码. 如果您在几分钟内没有收到电子邮件,请重试.", + "PasswordResetMailSentMessage": "帐户恢复电子邮件已发送到您的电子邮件地址. 如果您在15分钟内未在收件箱中看到此电子邮件,请检查垃圾邮件,并标记为非垃圾邮件.", + "ResetPassword": "重设密码", + "ConfirmPassword": "确认(重复)密码", + "ResetPassword_Information": "请输入您的新密码.", + "YourPasswordIsSuccessfullyReset": "您的密码已经被重置成功.", + "GoToTheApplication": "转到应用程序", + "BackToLogin": "返回登录" } } diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/ResetPasswordDto.cs b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/ResetPasswordDto.cs new file mode 100644 index 0000000000..2423e6c778 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/ResetPasswordDto.cs @@ -0,0 +1,18 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Auditing; + +namespace Volo.Abp.Account +{ + public class ResetPasswordDto + { + public Guid UserId { get; set; } + + [Required] + public string ResetToken { get; set; } + + [Required] + [DisableAuditing] + public string Password { get; set; } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/SendPasswordResetCodeDto.cs b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/SendPasswordResetCodeDto.cs new file mode 100644 index 0000000000..c4fcea4eec --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/SendPasswordResetCodeDto.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Identity; +using Volo.Abp.Validation; + +namespace Volo.Abp.Account +{ + public class SendPasswordResetCodeDto + { + [Required] + [EmailAddress] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] + public string Email { get; set; } + + [Required] + public string AppName { get; set; } + + public string ReturnUrl { get; set; } + + public string ReturnUrlHash { get; set; } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj b/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj index bdcf0c9e8d..5ee4aa8697 100644 --- a/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj +++ b/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj @@ -11,11 +11,17 @@ + + + + + + diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationModule.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationModule.cs index 1f2004ae7e..2130c52a12 100644 --- a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationModule.cs +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationModule.cs @@ -1,7 +1,8 @@ -using Volo.Abp.Identity; +using Volo.Abp.Emailing; +using Volo.Abp.Identity; using Volo.Abp.Modularity; -using Volo.Abp.UI.Navigation.Urls; using Volo.Abp.UI.Navigation; +using Volo.Abp.UI.Navigation.Urls; using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.Account @@ -9,7 +10,8 @@ namespace Volo.Abp.Account [DependsOn( typeof(AbpAccountApplicationContractsModule), typeof(AbpIdentityApplicationModule), - typeof(AbpUiNavigationModule) + typeof(AbpUiNavigationModule), + typeof(AbpEmailingModule) )] public class AbpAccountApplicationModule : AbpModule { @@ -19,6 +21,11 @@ namespace Volo.Abp.Account { options.FileSets.AddEmbedded(); }); + + Configure(options => + { + options.Applications["MVC"].Urls[AccountUrlNames.PasswordReset] = "Account/ResetPassword"; + }); } } -} \ No newline at end of file +} diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AccountAppService.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AccountAppService.cs index e2e4206b81..066ce47f64 100644 --- a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AccountAppService.cs +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AccountAppService.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; +using Volo.Abp.Account.Emailing; using Volo.Abp.Account.Settings; using Volo.Abp.Application.Services; using Volo.Abp.Identity; @@ -12,12 +13,18 @@ namespace Volo.Abp.Account { protected IIdentityRoleRepository RoleRepository { get; } protected IdentityUserManager UserManager { get; } + protected IAccountEmailer AccountEmailer { get; } + protected IdentitySecurityLogManager IdentitySecurityLogManager { get; } public AccountAppService( IdentityUserManager userManager, - IIdentityRoleRepository roleRepository) + IIdentityRoleRepository roleRepository, + IAccountEmailer accountEmailer, + IdentitySecurityLogManager identitySecurityLogManager) { RoleRepository = roleRepository; + AccountEmailer = accountEmailer; + IdentitySecurityLogManager = identitySecurityLogManager; UserManager = userManager; } @@ -35,6 +42,37 @@ namespace Volo.Abp.Account return ObjectMapper.Map(user); } + public virtual async Task SendPasswordResetCodeAsync(SendPasswordResetCodeDto input) + { + var user = await GetUserByEmail(input.Email); + var resetToken = await UserManager.GeneratePasswordResetTokenAsync(user); + await AccountEmailer.SendPasswordResetLinkAsync(user, resetToken, input.AppName, input.ReturnUrl, input.ReturnUrlHash); + } + + public virtual async Task ResetPasswordAsync(ResetPasswordDto input) + { + var user = await UserManager.GetByIdAsync(input.UserId); + (await UserManager.ResetPasswordAsync(user, input.ResetToken, input.Password)).CheckErrors(); + + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext + { + Identity = IdentitySecurityLogIdentityConsts.Identity, + Action = IdentitySecurityLogActionConsts.ChangePassword + }); + } + + protected virtual async Task GetUserByEmail(string email) + { + var user = await UserManager.FindByEmailAsync(email); + if (user == null) + { + throw new BusinessException("Volo.Account:InvalidEmailAddress") + .WithData("Email", email); + } + + return user; + } + protected virtual async Task CheckSelfRegistrationAsync() { if (!await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled)) @@ -43,4 +81,4 @@ namespace Volo.Abp.Account } } } -} \ No newline at end of file +} diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AccountUrlNames.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AccountUrlNames.cs new file mode 100644 index 0000000000..9ea1050ae4 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AccountUrlNames.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Account +{ + public static class AccountUrlNames + { + public const string PasswordReset = "Abp.Account.PasswordReset"; + } +} diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/AccountEmailer.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/AccountEmailer.cs new file mode 100644 index 0000000000..baa762c840 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/AccountEmailer.cs @@ -0,0 +1,106 @@ +using System; +using System.Diagnostics; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using System.Web; +using Microsoft.Extensions.Localization; +using Volo.Abp.Account.Emailing.Templates; +using Volo.Abp.Account.Localization; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Emailing; +using Volo.Abp.Identity; +using Volo.Abp.MultiTenancy; +using Volo.Abp.TextTemplating; +using Volo.Abp.UI.Navigation.Urls; + +namespace Volo.Abp.Account.Emailing +{ + public class AccountEmailer : IAccountEmailer, ITransientDependency + { + protected ITemplateRenderer TemplateRenderer { get; } + protected IEmailSender EmailSender { get; } + protected IStringLocalizer StringLocalizer { get; } + protected IAppUrlProvider AppUrlProvider { get; } + protected ICurrentTenant CurrentTenant { get; } + + public AccountEmailer( + IEmailSender emailSender, + ITemplateRenderer templateRenderer, + IStringLocalizer stringLocalizer, + IAppUrlProvider appUrlProvider, + ICurrentTenant currentTenant) + { + EmailSender = emailSender; + StringLocalizer = stringLocalizer; + AppUrlProvider = appUrlProvider; + CurrentTenant = currentTenant; + TemplateRenderer = templateRenderer; + } + + public virtual async Task SendPasswordResetLinkAsync( + IdentityUser user, + string resetToken, + string appName, + string returnUrl = null, + string returnUrlHash = null) + { + Debug.Assert(CurrentTenant.Id == user.TenantId, "This method can only work for current tenant!"); + + var url = await AppUrlProvider.GetResetPasswordUrlAsync(appName); + + var link = $"{url}?userId={user.Id}&tenantId={user.TenantId}&resetToken={UrlEncoder.Default.Encode(resetToken)}"; + + if (!returnUrl.IsNullOrEmpty()) + { + link += "&returnUrl=" + NormalizeReturnUrl(returnUrl); + } + + if (!returnUrlHash.IsNullOrEmpty()) + { + link += "&returnUrlHash=" + returnUrlHash; + } + + var emailContent = await TemplateRenderer.RenderAsync( + AccountEmailTemplates.PasswordResetLink, + new { link = link } + ); + + await EmailSender.SendAsync( + user.Email, + StringLocalizer["PasswordReset"], + emailContent + ); + } + + private string NormalizeReturnUrl(string returnUrl) + { + if (returnUrl.IsNullOrEmpty()) + { + return returnUrl; + } + + //Handling openid connect login + if (returnUrl.StartsWith("/connect/authorize/callback", StringComparison.OrdinalIgnoreCase)) + { + if (returnUrl.Contains("?")) + { + var queryPart = returnUrl.Split('?')[1]; + var queryParameters = queryPart.Split('&'); + foreach (var queryParameter in queryParameters) + { + if (queryParameter.Contains("=")) + { + var queryParam = queryParameter.Split('='); + if (queryParam[0] == "redirect_uri") + { + return HttpUtility.UrlDecode(queryParam[1]); + } + } + } + } + } + + return returnUrl; + } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/AppUrlProviderAccountExtensions.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/AppUrlProviderAccountExtensions.cs new file mode 100644 index 0000000000..571232824c --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/AppUrlProviderAccountExtensions.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; +using Volo.Abp.UI.Navigation.Urls; + +namespace Volo.Abp.Account.Emailing +{ + public static class AppUrlProviderAccountExtensions + { + public static Task GetResetPasswordUrlAsync(this IAppUrlProvider appUrlProvider, string appName) + { + return appUrlProvider.GetUrlAsync(appName, AccountUrlNames.PasswordReset); + } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/IAccountEmailer.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/IAccountEmailer.cs new file mode 100644 index 0000000000..46644f3872 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/IAccountEmailer.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Volo.Abp.Identity; + +namespace Volo.Abp.Account.Emailing +{ + public interface IAccountEmailer + { + Task SendPasswordResetLinkAsync( + IdentityUser user, + string resetToken, + string appName, + string returnUrl = null, + string returnUrlHash = null + ); + } +} diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/Templates/AccountEmailTemplateDefinitionProvider.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/Templates/AccountEmailTemplateDefinitionProvider.cs new file mode 100644 index 0000000000..6db5ad9e69 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/Templates/AccountEmailTemplateDefinitionProvider.cs @@ -0,0 +1,22 @@ +using Volo.Abp.Account.Localization; +using Volo.Abp.Emailing.Templates; +using Volo.Abp.Localization; +using Volo.Abp.TextTemplating; + +namespace Volo.Abp.Account.Emailing.Templates +{ + public class AccountEmailTemplateDefinitionProvider : TemplateDefinitionProvider + { + public override void Define(ITemplateDefinitionContext context) + { + context.Add( + new TemplateDefinition( + AccountEmailTemplates.PasswordResetLink, + displayName: LocalizableString.Create($"TextTemplate:{AccountEmailTemplates.PasswordResetLink}"), + layout: StandardEmailTemplates.Layout, + localizationResource: typeof(AccountResource) + ).WithVirtualFilePath("/Volo/Abp/Account/Emailing/Templates/PasswordResetLink.tpl", true) + ); + } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/Templates/AccountEmailTemplates.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/Templates/AccountEmailTemplates.cs new file mode 100644 index 0000000000..676064f0d6 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/Templates/AccountEmailTemplates.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Account.Emailing.Templates +{ + public static class AccountEmailTemplates + { + public const string PasswordResetLink = "Abp.Account.PasswordResetLink"; + } +} diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/Templates/PasswordResetLink.tpl b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/Templates/PasswordResetLink.tpl new file mode 100644 index 0000000000..05f21baf5b --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/Emailing/Templates/PasswordResetLink.tpl @@ -0,0 +1,7 @@ +

{{L "PasswordReset"}}

+ +

{{L "PasswordResetInfoInEmail"}}

+ + \ No newline at end of file diff --git a/modules/account/src/Volo.Abp.Account.HttpApi/Volo/Abp/Account/AccountController.cs b/modules/account/src/Volo.Abp.Account.HttpApi/Volo/Abp/Account/AccountController.cs index fa479568aa..d1f22587e8 100644 --- a/modules/account/src/Volo.Abp.Account.HttpApi/Volo/Abp/Account/AccountController.cs +++ b/modules/account/src/Volo.Abp.Account.HttpApi/Volo/Abp/Account/AccountController.cs @@ -23,5 +23,19 @@ namespace Volo.Abp.Account { return AccountAppService.RegisterAsync(input); } + + [HttpPost] + [Route("send-password-reset-code")] + public virtual Task SendPasswordResetCodeAsync(SendPasswordResetCodeDto input) + { + return AccountAppService.SendPasswordResetCodeAsync(input); + } + + [HttpPost] + [Route("reset-password")] + public virtual Task ResetPasswordAsync(ResetPasswordDto input) + { + return AccountAppService.ResetPasswordAsync(input); + } } -} \ No newline at end of file +} diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs index 273659fd69..bf76e1dbe3 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Identity; +using IdentityServer4.Configuration; +using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Identity.AspNetCore; using Volo.Abp.IdentityServer; @@ -33,6 +34,12 @@ namespace Volo.Abp.Account.Web options.FileSets.AddEmbedded(); }); + Configure(options => + { + options.UserInteraction.ConsentUrl = "/Consent"; + options.UserInteraction.ErrorUrl = "/Account/Error"; + }); + //TODO: Try to reuse from AbpIdentityAspNetCoreModule context.Services .AddAuthentication(o => diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Areas/Account/Controllers/ErrorController.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Areas/Account/Controllers/ErrorController.cs new file mode 100644 index 0000000000..d06efee337 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Areas/Account/Controllers/ErrorController.cs @@ -0,0 +1,63 @@ +using System.Net; +using System.Threading.Tasks; +using AutoMapper.Internal; +using IdentityServer4.Models; +using IdentityServer4.Services; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Views.Error; +using Volo.Abp.Http; + +namespace Volo.Abp.Account.Web.Areas.Account.Controllers +{ + [Area("account")] + public class ErrorController : AbpController + { + private readonly IIdentityServerInteractionService _interaction; + private readonly IWebHostEnvironment _environment; + private readonly AbpErrorPageOptions _abpErrorPageOptions; + + public ErrorController( + IIdentityServerInteractionService interaction, + IWebHostEnvironment environment, + IOptions abpErrorPageOptions) + { + _interaction = interaction; + _environment = environment; + _abpErrorPageOptions = abpErrorPageOptions.Value; + } + + public virtual async Task Index(string errorId) + { + var errorMessage = await _interaction.GetErrorContextAsync(errorId) ?? new ErrorMessage + { + Error = L["Error"] + }; + + if (!_environment.IsDevelopment()) + { + // Only show in development + errorMessage.ErrorDescription = null; + } + + const int statusCode = (int)HttpStatusCode.InternalServerError; + + return View(GetErrorPageUrl(statusCode), new AbpErrorViewModel + { + ErrorInfo = new RemoteServiceErrorInfo(errorMessage.Error, errorMessage.ErrorDescription), + HttpStatusCode = statusCode + }); + } + + protected virtual string GetErrorPageUrl(int statusCode) + { + var page = _abpErrorPageOptions.ErrorViewUrls.GetOrDefault(statusCode.ToString()); + + return string.IsNullOrWhiteSpace(page) ? "~/Views/Error/Default.cshtml" : page; + } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs index 297e54cd69..09392e50b1 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs @@ -17,9 +17,7 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.Identity; using Volo.Abp.Identity.AspNetCore; using Volo.Abp.MultiTenancy; -using Volo.Abp.SecurityLog; using Volo.Abp.Settings; -using Volo.Abp.Uow; namespace Volo.Abp.Account.Web.Pages.Account { diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj index 566d40b3f9..aca9729a31 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj @@ -20,8 +20,12 @@ + + + + diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs index 7e79442bd7..5fd2124f49 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs @@ -12,6 +12,7 @@ namespace Volo.Abp.Account.Web.Pages.Account { public abstract class AccountPageModel : AbpPageModel { + public IAccountAppService AccountAppService { get; set; } public SignInManager SignInManager { get; set; } public IdentityUserManager UserManager { get; set; } public IdentitySecurityLogManager IdentitySecurityLogManager { get; set; } diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ForgotPassword.cshtml b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ForgotPassword.cshtml new file mode 100644 index 0000000000..dd7d7586d0 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ForgotPassword.cshtml @@ -0,0 +1,21 @@ +@page +@inject IHtmlLocalizer L +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.Account.Localization +@model Volo.Abp.Account.Web.Pages.Account.ForgotPasswordModel +@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout +
+
+

@L["ForgotPassword"]

+
+

@L["SendPasswordResetLink_Information"]

+ + + + @L["Submit"] + + @L["Login"] + + +
+
diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ForgotPassword.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ForgotPassword.cshtml.cs new file mode 100644 index 0000000000..1f426fa80f --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ForgotPassword.cshtml.cs @@ -0,0 +1,51 @@ +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Identity; +using Volo.Abp.Validation; + +namespace Volo.Abp.Account.Web.Pages.Account +{ + public class ForgotPasswordModel : AccountPageModel + { + [Required] + [EmailAddress] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] + [BindProperty] + public string Email { get; set; } + + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ReturnUrl { get; set; } + + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ReturnUrlHash { get; set; } + + public virtual Task OnGetAsync() + { + return Task.FromResult(Page()); + } + + public virtual async Task OnPostAsync() + { + await AccountAppService.SendPasswordResetCodeAsync( + new SendPasswordResetCodeDto + { + Email = Email, + AppName = "MVC", //TODO: Const! + ReturnUrl = ReturnUrl, + ReturnUrlHash = ReturnUrlHash + } + ); + + return RedirectToPage( + "./PasswordResetLinkSent", + new + { + returnUrl = ReturnUrl, + returnUrlHash = ReturnUrlHash + }); + } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml index 41986c3856..056e69e895 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml @@ -31,12 +31,14 @@ -
- -
+ + + + + + @L["ForgotPassword"] + + @L["Login"] @if (Model.ShowCancelButton) { diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/PasswordResetLinkSent.cshtml b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/PasswordResetLinkSent.cshtml new file mode 100644 index 0000000000..8bd2e2ebb5 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/PasswordResetLinkSent.cshtml @@ -0,0 +1,15 @@ +@page +@model Volo.Abp.Account.Web.Pages.Account.PasswordResetLinkSentModel +@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.Account.Localization +@inject IHtmlLocalizer L +
+
+

@L["ForgotPassword"]

+
+

@L["PasswordResetMailSentMessage"]

+ ← @L["BackToLogin"] +
+
+
diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/PasswordResetLinkSent.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/PasswordResetLinkSent.cshtml.cs new file mode 100644 index 0000000000..ebf9c1b295 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/PasswordResetLinkSent.cshtml.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Volo.Abp.Account.Web.Pages.Account +{ + public class PasswordResetLinkSentModel : AccountPageModel + { + [BindProperty(SupportsGet = true)] + public string ReturnUrl { get; set; } + + [BindProperty(SupportsGet = true)] + public string ReturnUrlHash { get; set; } + + public virtual Task OnGetAsync() + { + return Task.FromResult(Page()); + } + + public virtual Task OnPostAsync() + { + return Task.FromResult(Page()); + } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPassword.cshtml b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPassword.cshtml new file mode 100644 index 0000000000..64d835497c --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPassword.cshtml @@ -0,0 +1,22 @@ +@page +@inject IHtmlLocalizer L +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.Account.Localization +@model Volo.Abp.Account.Web.Pages.Account.ResetPasswordModel +@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout +
+
+

@L["ResetPassword"]

+
+

@L["ResetPassword_Information"]

+ + + + + + + @L["Cancel"] + + +
+
diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPassword.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPassword.cshtml.cs new file mode 100644 index 0000000000..13370f5fbd --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPassword.cshtml.cs @@ -0,0 +1,110 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Auditing; +using Volo.Abp.Identity; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Validation; + +namespace Volo.Abp.Account.Web.Pages.Account +{ + //TODO: Implement live password complexity check on the razor view! + + public class ResetPasswordModel : AccountPageModel + { + [HiddenInput] + [BindProperty(SupportsGet = true)] + public Guid? TenantId { get; set; } + + [Required] + [HiddenInput] + [BindProperty(SupportsGet = true)] + public Guid UserId { get; set; } + + [Required] + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ResetToken { get; set; } + + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ReturnUrl { get; set; } + + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ReturnUrlHash { get; set; } + + [Required] + [BindProperty] + [DataType(DataType.Password)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] + [DisableAuditing] + public string Password { get; set; } + + [Required] + [BindProperty] + [DataType(DataType.Password)] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] + [DisableAuditing] + public string ConfirmPassword { get; set; } + + protected virtual ITenantResolveResultAccessor TenantResolveResultAccessor { get; } + + public ResetPasswordModel(ITenantResolveResultAccessor tenantResolveResultAccessor) + { + TenantResolveResultAccessor = tenantResolveResultAccessor; + } + + public virtual Task OnGetAsync() + { + //TODO: It would be good to try to switch tenant if needed + CheckCurrentTenant(TenantId); + return Task.FromResult(Page()); + } + + public virtual async Task OnPostAsync() + { + ValidateModel(); + + try + { + await AccountAppService.ResetPasswordAsync( + new ResetPasswordDto + { + UserId = UserId, + ResetToken = ResetToken, + Password = Password + } + ); + } + catch (AbpIdentityResultException e) + { + if (!string.IsNullOrWhiteSpace(e.Message)) + { + Alerts.Warning(e.Message); + return Page(); + } + + throw; + } + + //TODO: Try to automatically login! + return RedirectToPage("./ResetPasswordConfirmation", new + { + returnUrl = ReturnUrl, + returnUrlHash = ReturnUrlHash + }); + } + + protected override void ValidateModel() + { + if (!Equals(Password, ConfirmPassword)) + { + ModelState.AddModelError("ConfirmPassword", L["'{0}' and '{1}' do not match.", "ConfirmPassword", "Password"]); + } + + base.ValidateModel(); + } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPasswordConfirmation.cshtml b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPasswordConfirmation.cshtml new file mode 100644 index 0000000000..32c514656c --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPasswordConfirmation.cshtml @@ -0,0 +1,13 @@ +@page +@model Volo.Abp.Account.Web.Pages.Account.ResetPasswordConfirmationModel +@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.Account.Localization +@inject IHtmlLocalizer L +
+
+

@L["ResetPassword"]

+

@L["YourPasswordIsSuccessfullyReset"]

+ @L["GoToTheApplication"] +
+
diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPasswordConfirmation.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPasswordConfirmation.cshtml.cs new file mode 100644 index 0000000000..2b713c1577 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPasswordConfirmation.cshtml.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Volo.Abp.Account.Web.Pages.Account +{ + [AllowAnonymous] + public class ResetPasswordConfirmationModel : AccountPageModel + { + [BindProperty(SupportsGet = true)] + public string ReturnUrl { get; set; } + + [BindProperty(SupportsGet = true)] + public string ReturnUrlHash { get; set; } + + public virtual Task OnGetAsync() + { + ReturnUrl = GetRedirectUrl(ReturnUrl, ReturnUrlHash); + + return Task.FromResult(Page()); + } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj b/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj index e4bbaf00ba..d3f7979fb1 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj +++ b/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj @@ -22,10 +22,14 @@ + + + +
diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo.Abp.BlobStoring.Database.Domain.Shared.csproj b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo.Abp.BlobStoring.Database.Domain.Shared.csproj index d7f9289c12..ad171263fc 100644 --- a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo.Abp.BlobStoring.Database.Domain.Shared.csproj +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo.Abp.BlobStoring.Database.Domain.Shared.csproj @@ -9,7 +9,7 @@ - + diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo.Abp.BlobStoring.Database.Domain.csproj b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo.Abp.BlobStoring.Database.Domain.csproj index 9e52acf207..09f19c0cae 100644 --- a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo.Abp.BlobStoring.Database.Domain.csproj +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo.Abp.BlobStoring.Database.Domain.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.csproj b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.csproj index 20c155c894..ac1c336f15 100644 --- a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.csproj +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.csproj @@ -9,7 +9,7 @@ - + diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo.Abp.BlobStoring.Database.MongoDB.csproj b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo.Abp.BlobStoring.Database.MongoDB.csproj index dee196df48..2665bb6736 100644 --- a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo.Abp.BlobStoring.Database.MongoDB.csproj +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo.Abp.BlobStoring.Database.MongoDB.csproj @@ -9,7 +9,7 @@ - + diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Volo.Abp.BlobStoring.Database.TestBase.csproj b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Volo.Abp.BlobStoring.Database.TestBase.csproj index 196cdce7bb..bbf0bad776 100644 --- a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Volo.Abp.BlobStoring.Database.TestBase.csproj +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Volo.Abp.BlobStoring.Database.TestBase.csproj @@ -12,10 +12,10 @@ - - - - + + + + diff --git a/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj b/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj index 4fb4e461af..5d56d83ec9 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj +++ b/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj @@ -17,7 +17,7 @@ - + diff --git a/modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj b/modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj index 16520deaf6..96c7d134ba 100644 --- a/modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj +++ b/modules/blogging/src/Volo.Blogging.Admin.Web/Volo.Blogging.Admin.Web.csproj @@ -30,8 +30,12 @@ + + + + diff --git a/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj b/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj index 511b2b5d39..473cfb11c9 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj +++ b/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj @@ -30,8 +30,12 @@ + + + + diff --git a/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj b/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj index aa3ecf2dac..217b57addf 100644 --- a/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj +++ b/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj @@ -23,9 +23,15 @@ + + + + + + diff --git a/modules/cms-kit/host/Volo.CmsKit.Host.Shared/FeatureConfigurer.cs b/modules/cms-kit/host/Volo.CmsKit.Host.Shared/FeatureConfigurer.cs new file mode 100644 index 0000000000..9046c60aca --- /dev/null +++ b/modules/cms-kit/host/Volo.CmsKit.Host.Shared/FeatureConfigurer.cs @@ -0,0 +1,18 @@ +using Volo.Abp.GlobalFeatures; +using Volo.Abp.Threading; + +namespace Volo.CmsKit +{ + public static class FeatureConfigurer + { + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.CmsKit().EnableAll(); + }); + } + } +} diff --git a/modules/cms-kit/host/Volo.CmsKit.Host.Shared/Volo.CmsKit.Host.Shared.csproj b/modules/cms-kit/host/Volo.CmsKit.Host.Shared/Volo.CmsKit.Host.Shared.csproj index 3f8c167b98..c6a4484865 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Host.Shared/Volo.CmsKit.Host.Shared.csproj +++ b/modules/cms-kit/host/Volo.CmsKit.Host.Shared/Volo.CmsKit.Host.Shared.csproj @@ -7,4 +7,8 @@ Volo.CmsKit + + + + diff --git a/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/CmsKitHttpApiHostModule.cs b/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/CmsKitHttpApiHostModule.cs index c63d28081f..f1a9b8568f 100644 --- a/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/CmsKitHttpApiHostModule.cs +++ b/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/CmsKitHttpApiHostModule.cs @@ -52,6 +52,11 @@ namespace Volo.CmsKit { private const string DefaultCorsPolicyName = "Default"; + public override void PreConfigureServices(ServiceConfigurationContext context) + { + FeatureConfigurer.Configure(); + } + public override void ConfigureServices(ServiceConfigurationContext context) { var hostingEnvironment = context.Services.GetHostingEnvironment(); @@ -163,7 +168,7 @@ namespace Volo.CmsKit app.UseCorrelationId(); app.UseVirtualFiles(); app.UseRouting(); - app.UseCors(DefaultCorsPolicyName); + app.UseCors(DefaultCorsPolicyName); app.UseAuthentication(); app.UseAbpClaimsMap(); if (MultiTenancyConsts.IsEnabled) diff --git a/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/EntityFrameworkCore/CmsKitHttpApiHostMigrationsDbContextFactory.cs b/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/EntityFrameworkCore/CmsKitHttpApiHostMigrationsDbContextFactory.cs index 3a6dbb47ad..dc19e09635 100644 --- a/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/EntityFrameworkCore/CmsKitHttpApiHostMigrationsDbContextFactory.cs +++ b/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/EntityFrameworkCore/CmsKitHttpApiHostMigrationsDbContextFactory.cs @@ -9,6 +9,8 @@ namespace Volo.CmsKit.EntityFrameworkCore { public CmsKitHttpApiHostMigrationsDbContext CreateDbContext(string[] args) { + FeatureConfigurer.Configure(); + var configuration = BuildConfiguration(); var builder = new DbContextOptionsBuilder() diff --git a/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Volo.CmsKit.HttpApi.Host.csproj b/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Volo.CmsKit.HttpApi.Host.csproj index 2a0c5e57c1..9fa7c4be20 100644 --- a/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Volo.CmsKit.HttpApi.Host.csproj +++ b/modules/cms-kit/host/Volo.CmsKit.HttpApi.Host/Volo.CmsKit.HttpApi.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/CmsKitIdentityServerModule.cs b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/CmsKitIdentityServerModule.cs index faf90a3fc2..b9afe934a3 100644 --- a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/CmsKitIdentityServerModule.cs +++ b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/CmsKitIdentityServerModule.cs @@ -77,6 +77,11 @@ namespace Volo.CmsKit { private const string DefaultCorsPolicyName = "Default"; + public override void PreConfigureServices(ServiceConfigurationContext context) + { + FeatureConfigurer.Configure(); + } + public override void ConfigureServices(ServiceConfigurationContext context) { var hostingEnvironment = context.Services.GetHostingEnvironment(); @@ -182,10 +187,10 @@ namespace Volo.CmsKit app.UseCorrelationId(); app.UseVirtualFiles(); app.UseRouting(); - app.UseCors(DefaultCorsPolicyName); + app.UseCors(DefaultCorsPolicyName); app.UseAuthentication(); app.UseJwtTokenMiddleware(); - + if (MultiTenancyConsts.IsEnabled) { app.UseMultiTenancy(); diff --git a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/EntityFrameworkCore/IdentityServerHostMigrationsDbContextFactory.cs b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/EntityFrameworkCore/IdentityServerHostMigrationsDbContextFactory.cs index f6e80d62ec..19402b80c1 100644 --- a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/EntityFrameworkCore/IdentityServerHostMigrationsDbContextFactory.cs +++ b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/EntityFrameworkCore/IdentityServerHostMigrationsDbContextFactory.cs @@ -9,6 +9,8 @@ namespace Volo.CmsKit.EntityFrameworkCore { public IdentityServerHostMigrationsDbContext CreateDbContext(string[] args) { + FeatureConfigurer.Configure(); + var configuration = BuildConfiguration(); var builder = new DbContextOptionsBuilder() diff --git a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/Pages/Index.cshtml b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/Pages/Index.cshtml index 73f229b4f3..522b6ebdf7 100644 --- a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/Pages/Index.cshtml +++ b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/Pages/Index.cshtml @@ -9,7 +9,7 @@ - Logout + Logout

@CurrentUser.UserName

@@ -31,4 +31,4 @@

Login -} \ No newline at end of file +} diff --git a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/Volo.CmsKit.IdentityServer.csproj b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/Volo.CmsKit.IdentityServer.csproj index da90915bf2..b26c2bbe06 100644 --- a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/Volo.CmsKit.IdentityServer.csproj +++ b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/Volo.CmsKit.IdentityServer.csproj @@ -10,7 +10,7 @@ - + diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Host/CmsKitWebHostModule.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Host/CmsKitWebHostModule.cs index 95946c54df..7587b075d0 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Host/CmsKitWebHostModule.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Host/CmsKitWebHostModule.cs @@ -65,6 +65,8 @@ namespace Volo.CmsKit { public override void PreConfigureServices(ServiceConfigurationContext context) { + FeatureConfigurer.Configure(); + context.Services.PreConfigure(options => { options.AddAssemblyResource( @@ -222,7 +224,7 @@ namespace Volo.CmsKit app.UseHttpsRedirection(); app.UseVirtualFiles(); app.UseRouting(); - app.UseAuthentication(); + app.UseAuthentication(); if (MultiTenancyConsts.IsEnabled) { diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Host/Volo.CmsKit.Web.Host.csproj b/modules/cms-kit/host/Volo.CmsKit.Web.Host/Volo.CmsKit.Web.Host.csproj index 46b83d5180..e58aa309c6 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Host/Volo.CmsKit.Web.Host.csproj +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Host/Volo.CmsKit.Web.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs index 3579043302..08dadb3dd6 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs @@ -63,6 +63,11 @@ namespace Volo.CmsKit )] public class CmsKitWebUnifiedModule : AbpModule { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + FeatureConfigurer.Configure(); + } + public override void ConfigureServices(ServiceConfigurationContext context) { var hostingEnvironment = context.Services.GetHostingEnvironment(); @@ -113,11 +118,6 @@ namespace Volo.CmsKit { options.IsEnabled = MultiTenancyConsts.IsEnabled; }); - - Configure(options => - { - options.PublicCommentEntities.Add("publicQuote"); - }); } public override void OnApplicationInitialization(ApplicationInitializationContext context) diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/EntityFrameworkCore/UnifiedDbContextFactory.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/EntityFrameworkCore/UnifiedDbContextFactory.cs index f2c1d8a065..170e4dc688 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/EntityFrameworkCore/UnifiedDbContextFactory.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/EntityFrameworkCore/UnifiedDbContextFactory.cs @@ -9,6 +9,8 @@ namespace Volo.CmsKit.EntityFrameworkCore { public UnifiedDbContext CreateDbContext(string[] args) { + FeatureConfigurer.Configure(); + var configuration = BuildConfiguration(); var builder = new DbContextOptionsBuilder() diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200807070949_Reaction_Comment_Multitenancy.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200807070949_Reaction_Comment_Multitenancy.cs deleted file mode 100644 index 42f880e3b4..0000000000 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200807070949_Reaction_Comment_Multitenancy.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace Volo.CmsKit.Migrations -{ - public partial class Reaction_Comment_Multitenancy : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "TenantId", - table: "CmsUserReactions", - nullable: true); - - migrationBuilder.AddColumn( - name: "TenantId", - table: "CmsComments", - nullable: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "TenantId", - table: "CmsUserReactions"); - - migrationBuilder.DropColumn( - name: "TenantId", - table: "CmsComments"); - } - } -} diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200807070949_Reaction_Comment_Multitenancy.Designer.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200813130355_Initial.Designer.cs similarity index 99% rename from modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200807070949_Reaction_Comment_Multitenancy.Designer.cs rename to modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200813130355_Initial.Designer.cs index addf1e46c6..69edfcf69f 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200807070949_Reaction_Comment_Multitenancy.Designer.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200813130355_Initial.Designer.cs @@ -11,15 +11,15 @@ using Volo.CmsKit.EntityFrameworkCore; namespace Volo.CmsKit.Migrations { [DbContext(typeof(UnifiedDbContext))] - [Migration("20200807070949_Reaction_Comment_Multitenancy")] - partial class Reaction_Comment_Multitenancy + [Migration("20200813130355_Initial")] + partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "3.1.5") + .HasAnnotation("ProductVersion", "3.1.6") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200810022644_Initial.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200813130355_Initial.cs similarity index 99% rename from modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200810022644_Initial.cs rename to modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200813130355_Initial.cs index 6f8b2a069c..9f2560f9c0 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200810022644_Initial.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200813130355_Initial.cs @@ -200,6 +200,7 @@ namespace Volo.CmsKit.Migrations columns: table => new { Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), EntityType = table.Column(maxLength: 64, nullable: false), EntityId = table.Column(maxLength: 64, nullable: false), Text = table.Column(maxLength: 512, nullable: false), @@ -217,6 +218,7 @@ namespace Volo.CmsKit.Migrations columns: table => new { Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), EntityType = table.Column(maxLength: 64, nullable: false), EntityId = table.Column(maxLength: 64, nullable: false), ReactionName = table.Column(maxLength: 32, nullable: false), diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200810022644_Initial.Designer.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200815101508_ReArrange_Indexes.Designer.cs similarity index 98% rename from modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200810022644_Initial.Designer.cs rename to modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200815101508_ReArrange_Indexes.Designer.cs index a29b41671a..53c6d48621 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200810022644_Initial.Designer.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200815101508_ReArrange_Indexes.Designer.cs @@ -11,8 +11,8 @@ using Volo.CmsKit.EntityFrameworkCore; namespace Volo.CmsKit.Migrations { [DbContext(typeof(UnifiedDbContext))] - [Migration("20200810022644_Initial")] - partial class Initial + [Migration("20200815101508_ReArrange_Indexes")] + partial class ReArrange_Indexes { protected override void BuildTargetModel(ModelBuilder modelBuilder) { @@ -941,6 +941,10 @@ namespace Volo.CmsKit.Migrations b.Property("RepliedCommentId") .HasColumnType("uniqueidentifier"); + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + b.Property("Text") .IsRequired() .HasColumnType("nvarchar(512)") @@ -948,9 +952,9 @@ namespace Volo.CmsKit.Migrations b.HasKey("Id"); - b.HasIndex("RepliedCommentId"); + b.HasIndex("TenantId", "RepliedCommentId"); - b.HasIndex("EntityType", "EntityId"); + b.HasIndex("TenantId", "EntityType", "EntityId"); b.ToTable("CmsComments"); }); @@ -984,11 +988,15 @@ namespace Volo.CmsKit.Migrations .HasColumnType("nvarchar(32)") .HasMaxLength(32); + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + b.HasKey("Id"); - b.HasIndex("EntityType", "EntityId"); + b.HasIndex("TenantId", "EntityType", "EntityId", "ReactionName"); - b.HasIndex("CreatorId", "EntityType", "EntityId", "ReactionName"); + b.HasIndex("TenantId", "CreatorId", "EntityType", "EntityId", "ReactionName"); b.ToTable("CmsUserReactions"); }); @@ -1054,6 +1062,10 @@ namespace Volo.CmsKit.Migrations b.HasKey("Id"); + b.HasIndex("TenantId", "Email"); + + b.HasIndex("TenantId", "UserName"); + b.ToTable("CmsUsers"); }); diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200815101508_ReArrange_Indexes.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200815101508_ReArrange_Indexes.cs new file mode 100644 index 0000000000..ed957d0d8a --- /dev/null +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200815101508_ReArrange_Indexes.cs @@ -0,0 +1,103 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Volo.CmsKit.Migrations +{ + public partial class ReArrange_Indexes : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_CmsUserReactions_EntityType_EntityId", + table: "CmsUserReactions"); + + migrationBuilder.DropIndex( + name: "IX_CmsUserReactions_CreatorId_EntityType_EntityId_ReactionName", + table: "CmsUserReactions"); + + migrationBuilder.DropIndex( + name: "IX_CmsComments_RepliedCommentId", + table: "CmsComments"); + + migrationBuilder.DropIndex( + name: "IX_CmsComments_EntityType_EntityId", + table: "CmsComments"); + + migrationBuilder.CreateIndex( + name: "IX_CmsUsers_TenantId_Email", + table: "CmsUsers", + columns: new[] { "TenantId", "Email" }); + + migrationBuilder.CreateIndex( + name: "IX_CmsUsers_TenantId_UserName", + table: "CmsUsers", + columns: new[] { "TenantId", "UserName" }); + + migrationBuilder.CreateIndex( + name: "IX_CmsUserReactions_TenantId_EntityType_EntityId_ReactionName", + table: "CmsUserReactions", + columns: new[] { "TenantId", "EntityType", "EntityId", "ReactionName" }); + + migrationBuilder.CreateIndex( + name: "IX_CmsUserReactions_TenantId_CreatorId_EntityType_EntityId_ReactionName", + table: "CmsUserReactions", + columns: new[] { "TenantId", "CreatorId", "EntityType", "EntityId", "ReactionName" }); + + migrationBuilder.CreateIndex( + name: "IX_CmsComments_TenantId_RepliedCommentId", + table: "CmsComments", + columns: new[] { "TenantId", "RepliedCommentId" }); + + migrationBuilder.CreateIndex( + name: "IX_CmsComments_TenantId_EntityType_EntityId", + table: "CmsComments", + columns: new[] { "TenantId", "EntityType", "EntityId" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_CmsUsers_TenantId_Email", + table: "CmsUsers"); + + migrationBuilder.DropIndex( + name: "IX_CmsUsers_TenantId_UserName", + table: "CmsUsers"); + + migrationBuilder.DropIndex( + name: "IX_CmsUserReactions_TenantId_EntityType_EntityId_ReactionName", + table: "CmsUserReactions"); + + migrationBuilder.DropIndex( + name: "IX_CmsUserReactions_TenantId_CreatorId_EntityType_EntityId_ReactionName", + table: "CmsUserReactions"); + + migrationBuilder.DropIndex( + name: "IX_CmsComments_TenantId_RepliedCommentId", + table: "CmsComments"); + + migrationBuilder.DropIndex( + name: "IX_CmsComments_TenantId_EntityType_EntityId", + table: "CmsComments"); + + migrationBuilder.CreateIndex( + name: "IX_CmsUserReactions_EntityType_EntityId", + table: "CmsUserReactions", + columns: new[] { "EntityType", "EntityId" }); + + migrationBuilder.CreateIndex( + name: "IX_CmsUserReactions_CreatorId_EntityType_EntityId_ReactionName", + table: "CmsUserReactions", + columns: new[] { "CreatorId", "EntityType", "EntityId", "ReactionName" }); + + migrationBuilder.CreateIndex( + name: "IX_CmsComments_RepliedCommentId", + table: "CmsComments", + column: "RepliedCommentId"); + + migrationBuilder.CreateIndex( + name: "IX_CmsComments_EntityType_EntityId", + table: "CmsComments", + columns: new[] { "EntityType", "EntityId" }); + } + } +} diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs index a0684b4eb6..931ed2b852 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs @@ -950,9 +950,9 @@ namespace Volo.CmsKit.Migrations b.HasKey("Id"); - b.HasIndex("RepliedCommentId"); + b.HasIndex("TenantId", "RepliedCommentId"); - b.HasIndex("EntityType", "EntityId"); + b.HasIndex("TenantId", "EntityType", "EntityId"); b.ToTable("CmsComments"); }); @@ -992,9 +992,9 @@ namespace Volo.CmsKit.Migrations b.HasKey("Id"); - b.HasIndex("EntityType", "EntityId"); + b.HasIndex("TenantId", "EntityType", "EntityId", "ReactionName"); - b.HasIndex("CreatorId", "EntityType", "EntityId", "ReactionName"); + b.HasIndex("TenantId", "CreatorId", "EntityType", "EntityId", "ReactionName"); b.ToTable("CmsUserReactions"); }); @@ -1060,6 +1060,10 @@ namespace Volo.CmsKit.Migrations b.HasKey("Id"); + b.HasIndex("TenantId", "Email"); + + b.HasIndex("TenantId", "UserName"); + b.ToTable("CmsUsers"); }); diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml index 5c6dabfd19..253016f03b 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml @@ -1,6 +1,8 @@ @page @using Localization.Resources.AbpUi @using Microsoft.Extensions.Localization +@using Volo.Abp.GlobalFeatures +@using Volo.CmsKit.GlobalFeatures @using Volo.CmsKit.Pages @using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting @using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.ReactionSelection @@ -20,9 +22,15 @@ - @await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "quote", entityId = "1" }) -
- @await Component.InvokeAsync(typeof(CommentingViewComponent), new { entityType = "quote", entityId = "1" }) + @if (GlobalFeatureManager.Instance.IsEnabled()) + { + @await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new {entityType = "quote", entityId = "1"}) +
+ } + @if (GlobalFeatureManager.Instance.IsEnabled()) + { + @await Component.InvokeAsync(typeof(CommentingViewComponent), new {entityType = "quote", entityId = "1"}) + }
@@ -38,8 +46,14 @@ - @await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "quote", entityId = "2" }) -
- @await Component.InvokeAsync(typeof(CommentingViewComponent), new { entityType = "quote", entityId = "2" }) + @if (GlobalFeatureManager.Instance.IsEnabled()) + { + @await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "quote", entityId = "2" }) +
+ } + @if (GlobalFeatureManager.Instance.IsEnabled()) + { + @await Component.InvokeAsync(typeof(CommentingViewComponent), new { entityType = "quote", entityId = "2" }) + }
diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml.cs index e6ac4303db..1b9f50993f 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml.cs @@ -1,13 +1,9 @@ -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc.RazorPages; namespace Volo.CmsKit.Pages { - [Authorize] public class IndexModel : PageModel { - public void OnGet() - { - } + } } diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Public.cshtml b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Public.cshtml deleted file mode 100644 index 1846666943..0000000000 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Public.cshtml +++ /dev/null @@ -1,39 +0,0 @@ -@page -@using Localization.Resources.AbpUi -@using Microsoft.Extensions.Localization -@using Volo.CmsKit.Pages -@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting -@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.ReactionSelection -@model PublicModel -@inject IStringLocalizer Localizer -

CMS Kit DEMO - Public

- - - - -

- Any fool can write code that a computer can understand. Good programmers write code that humans can understand. -

-
- Martin Fowler -
-
-
- @await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "publicQuote", entityId = "1" }) -
- @await Component.InvokeAsync(typeof(CommentingViewComponent), new { entityType = "publicQuote", entityId = "1", loginUrl = "Account/Login?ReturnUrl=%2Fpublic" }) -
- - - - -

- Writing code is very simple, but writing simple code is the hardest thing there is! -

-
- Halil ibrahim Kalkan (inspired from Johan Cruyff) -
-
-
- @await Component.InvokeAsync(typeof(CommentingViewComponent), new { entityType = "publicQuote", entityId = "2", loginUrl="Account/Login?ReturnUrl=%2Fpublic" }) -
diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Public.cshtml.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Public.cshtml.cs deleted file mode 100644 index 3ec2b2020a..0000000000 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Public.cshtml.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace Volo.CmsKit.Pages -{ - public class PublicModel : PageModel - { - public void OnGet() - { - } - } -} diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Volo.CmsKit.Web.Unified.csproj b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Volo.CmsKit.Web.Unified.csproj index d500cbce2d..3b5450be78 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Volo.CmsKit.Web.Unified.csproj +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Volo.CmsKit.Web.Unified.csproj @@ -12,7 +12,7 @@ - + @@ -47,13 +47,6 @@ - - true - PreserveNewest - - - public.cshtml - Public.cshtml diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Permissions/CmsKitAdminPermissionDefinitionProvider.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Permissions/CmsKitAdminPermissionDefinitionProvider.cs deleted file mode 100644 index 61c9f990d4..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Permissions/CmsKitAdminPermissionDefinitionProvider.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Volo.Abp.Authorization.Permissions; -using Volo.Abp.Localization; -using Volo.CmsKit.Localization; - -namespace Volo.CmsKit.Admin.Permissions -{ - public class CmsKitAdminPermissionDefinitionProvider : PermissionDefinitionProvider - { - public override void Define(IPermissionDefinitionContext context) - { - var myGroup = context.AddGroup(CmsKitAdminPermissions.GroupName, L("Permission:CmsKit.Admin")); - } - - private static LocalizableString L(string name) - { - return LocalizableString.Create(name); - } - } -} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Permissions/CmsKitAdminPermissions.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Permissions/CmsKitAdminPermissions.cs deleted file mode 100644 index 10e3a4e1d8..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Permissions/CmsKitAdminPermissions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Volo.Abp.Reflection; - -namespace Volo.CmsKit.Admin.Permissions -{ - public class CmsKitAdminPermissions - { - public const string GroupName = "CmsKit.Admin"; - - public static string[] GetAll() - { - return ReflectionHelper.GetPublicConstantsRecursively(typeof(CmsKitAdminPermissions)); - } - } -} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs similarity index 53% rename from modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppService.cs rename to modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs index 8ef58a46a6..3f4596f586 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs @@ -1,8 +1,8 @@ namespace Volo.CmsKit.Admin { - public abstract class CmsKitAdminAppService : CmsKitAppService + public abstract class CmsKitAdminAppServiceBase : CmsKitAppServiceBase { - protected CmsKitAdminAppService() + protected CmsKitAdminAppServiceBase() { ObjectMapperContext = typeof(CmsKitAdminApplicationModule); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/CmsKitAdminWebModule.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/CmsKitAdminWebModule.cs index af2398a030..f8ae091733 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/CmsKitAdminWebModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/CmsKitAdminWebModule.cs @@ -21,7 +21,12 @@ namespace Volo.CmsKit.Admin.Web { context.Services.PreConfigure(options => { - options.AddAssemblyResource(typeof(CmsKitResource), typeof(CmsKitAdminWebModule).Assembly); + options.AddAssemblyResource( + typeof(CmsKitResource), + typeof(CmsKitAdminWebModule).Assembly, + typeof(CmsKitAdminApplicationContractsModule).Assembly, + typeof(CmsKitCommonApplicationContractsModule).Assembly + ); }); PreConfigure(mvcBuilder => diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Volo.CmsKit.Admin.Web.csproj b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Volo.CmsKit.Admin.Web.csproj index f10f38c18c..6895d6e0bd 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Volo.CmsKit.Admin.Web.csproj +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Volo.CmsKit.Admin.Web.csproj @@ -25,8 +25,12 @@ + + + + diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Permissions/CmsKitPublicPermissionDefinitionProvider.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Permissions/CmsKitPermissionDefinitionProvider.cs similarity index 72% rename from modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Permissions/CmsKitPublicPermissionDefinitionProvider.cs rename to modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Permissions/CmsKitPermissionDefinitionProvider.cs index 2baf544a69..c0f7775ba8 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Permissions/CmsKitPublicPermissionDefinitionProvider.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Permissions/CmsKitPermissionDefinitionProvider.cs @@ -2,14 +2,14 @@ using Volo.Abp.Localization; using Volo.CmsKit.Localization; -namespace Volo.CmsKit.Public.Permissions +namespace Volo.CmsKit.Permissions { - public class CmsKitPublicPermissionDefinitionProvider : PermissionDefinitionProvider + public class CmsKitPermissionDefinitionProvider : PermissionDefinitionProvider { public override void Define(IPermissionDefinitionContext context) { var myGroup = context.AddGroup( - CmsKitPublicPermissions.GroupName, + CmsKitPermissions.GroupName, L("Permission:CmsKit.Public") ); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Permissions/CmsKitPublicPermissions.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Permissions/CmsKitPermissions.cs similarity index 65% rename from modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Permissions/CmsKitPublicPermissions.cs rename to modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Permissions/CmsKitPermissions.cs index 6b348ee430..08557bb3ca 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Permissions/CmsKitPublicPermissions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Permissions/CmsKitPermissions.cs @@ -1,14 +1,14 @@ using Volo.Abp.Reflection; -namespace Volo.CmsKit.Public.Permissions +namespace Volo.CmsKit.Permissions { - public class CmsKitPublicPermissions + public class CmsKitPermissions { public const string GroupName = "CmsKit.Public"; public static string[] GetAll() { - return ReflectionHelper.GetPublicConstantsRecursively(typeof(CmsKitPublicPermissions)); + return ReflectionHelper.GetPublicConstantsRecursively(typeof(CmsKitPermissions)); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitAdminAppService.cs index 7f8d7c8ab8..ffb2a8b197 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitAdminAppService.cs @@ -3,9 +3,9 @@ using Volo.CmsKit.Localization; namespace Volo.CmsKit { - public abstract class CmsKitAppService : ApplicationService + public abstract class CmsKitAppServiceBase : ApplicationService { - protected CmsKitAppService() + protected CmsKitAppServiceBase() { LocalizationResource = typeof(CmsKitResource); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.HttpApi/Volo/CmsKit/Controllers/CmsKitControllerBase.cs b/modules/cms-kit/src/Volo.CmsKit.Common.HttpApi/Volo/CmsKit/Controllers/CmsKitControllerBase.cs index 8938109b3b..21a546195a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Common.HttpApi/Volo/CmsKit/Controllers/CmsKitControllerBase.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Common.HttpApi/Volo/CmsKit/Controllers/CmsKitControllerBase.cs @@ -3,9 +3,9 @@ using Volo.CmsKit.Localization; namespace Volo.CmsKit.Controllers { - public class CmsKitControllerBase : AbpController + public abstract class CmsKitControllerBase : AbpController { - public CmsKitControllerBase() + protected CmsKitControllerBase() { LocalizationResource = typeof(CmsKitResource); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.Web/CmsKitOptions.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Web/CmsKitUiOptions.cs similarity index 100% rename from modules/cms-kit/src/Volo.CmsKit.Common.Web/CmsKitOptions.cs rename to modules/cms-kit/src/Volo.CmsKit.Common.Web/CmsKitUiOptions.cs diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.Web/Volo.CmsKit.Common.Web.csproj b/modules/cms-kit/src/Volo.CmsKit.Common.Web/Volo.CmsKit.Common.Web.csproj index b1a67ff399..82ccec869f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Common.Web/Volo.CmsKit.Common.Web.csproj +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Web/Volo.CmsKit.Common.Web.csproj @@ -29,6 +29,10 @@ + + + + diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo.CmsKit.Domain.Shared.csproj b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo.CmsKit.Domain.Shared.csproj index 03bc3bce5d..e5e84b7aca 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo.CmsKit.Domain.Shared.csproj +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo.CmsKit.Domain.Shared.csproj @@ -11,6 +11,7 @@ + diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/Abp/GlobalFeatures/GlobalModuleFeaturesDictionaryCmsKitExtensions.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/Abp/GlobalFeatures/GlobalModuleFeaturesDictionaryCmsKitExtensions.cs new file mode 100644 index 0000000000..72701d5e4d --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/Abp/GlobalFeatures/GlobalModuleFeaturesDictionaryCmsKitExtensions.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using Volo.CmsKit.GlobalFeatures; + +namespace Volo.Abp.GlobalFeatures +{ + public static class GlobalModuleFeaturesDictionaryCmsKitExtensions + { + public static GlobalCmsKitFeatures CmsKit( + [NotNull] this GlobalModuleFeaturesDictionary modules) + { + Check.NotNull(modules, nameof(modules)); + + return modules + .GetOrAdd( + GlobalCmsKitFeatures.ModuleName, + _ => new GlobalCmsKitFeatures(modules.FeatureManager) + ) + as GlobalCmsKitFeatures; + } + + public static GlobalModuleFeaturesDictionary CmsKit( + [NotNull] this GlobalModuleFeaturesDictionary modules, + [NotNull] Action configureAction) + { + Check.NotNull(configureAction, nameof(configureAction)); + + configureAction(modules.CmsKit()); + + return modules; + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitDomainSharedModule.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitDomainSharedModule.cs index 925acdaf79..d2490cfb37 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitDomainSharedModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitDomainSharedModule.cs @@ -1,4 +1,6 @@ -using Volo.Abp.Modularity; +using System; +using Volo.Abp.GlobalFeatures; +using Volo.Abp.Modularity; using Volo.Abp.Localization; using Volo.CmsKit.Localization; using Volo.Abp.Localization.ExceptionHandling; @@ -9,7 +11,8 @@ using Volo.Abp.VirtualFileSystem; namespace Volo.CmsKit { [DependsOn( - typeof(AbpValidationModule) + typeof(AbpValidationModule), + typeof(AbpGlobalFeaturesModule) )] public class CmsKitDomainSharedModule : AbpModule { diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CommentConsts.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CommentConsts.cs index 9fed054cae..68091c7f2d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CommentConsts.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CommentConsts.cs @@ -1,10 +1,12 @@ -namespace Volo.CmsKit.Comments +using Volo.CmsKit.Entities; + +namespace Volo.CmsKit.Comments { public static class CommentConsts { - public static int EntityTypeLength { get; set; } = 64; + public static int MaxEntityTypeLength { get; set; } = CmsEntityConsts.MaxEntityTypeLength; - public static int EntityIdLength { get; set; } = 64; + public static int MaxEntityIdLength { get; set; } = CmsEntityConsts.MaxEntityIdLength; public static int MaxTextLength { get; set; } = 512; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Entities/CmsEntityConsts.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Entities/CmsEntityConsts.cs new file mode 100644 index 0000000000..7176240196 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Entities/CmsEntityConsts.cs @@ -0,0 +1,9 @@ +namespace Volo.CmsKit.Entities +{ + public class CmsEntityConsts + { + public static int MaxEntityTypeLength { get; set; } = 64; + + public static int MaxEntityIdLength { get; set; } = 64; + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/CommentsFeature.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/CommentsFeature.cs new file mode 100644 index 0000000000..a812bd5388 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/CommentsFeature.cs @@ -0,0 +1,18 @@ +using JetBrains.Annotations; +using Volo.Abp.GlobalFeatures; + +namespace Volo.CmsKit.GlobalFeatures +{ + [GlobalFeatureName(Name)] + public class CommentsFeature : GlobalFeature + { + public const string Name = "CmsKit.Comments"; + + internal CommentsFeature( + [NotNull] GlobalCmsKitFeatures cmsKit + ) : base(cmsKit) + { + + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/GlobalCmsKitFeatures.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/GlobalCmsKitFeatures.cs new file mode 100644 index 0000000000..646a91ef4f --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/GlobalCmsKitFeatures.cs @@ -0,0 +1,21 @@ +using JetBrains.Annotations; +using Volo.Abp.GlobalFeatures; + +namespace Volo.CmsKit.GlobalFeatures +{ + public class GlobalCmsKitFeatures : GlobalModuleFeatures + { + public const string ModuleName = "CmsKit"; + + public ReactionsFeature Reactions => GetFeature(); + + public CommentsFeature Comments => GetFeature(); + + public GlobalCmsKitFeatures([NotNull] GlobalFeatureManager featureManager) + : base(featureManager) + { + AddFeature(new ReactionsFeature(this)); + AddFeature(new CommentsFeature(this)); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/ReactionsFeature.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/ReactionsFeature.cs new file mode 100644 index 0000000000..ad9a60b64d --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/ReactionsFeature.cs @@ -0,0 +1,17 @@ +using JetBrains.Annotations; +using Volo.Abp.GlobalFeatures; + +namespace Volo.CmsKit.GlobalFeatures +{ + [GlobalFeatureName(Name)] + public class ReactionsFeature : GlobalFeature + { + public const string Name = "CmsKit.Reactions"; + + internal ReactionsFeature( + [NotNull] GlobalCmsKitFeatures cmsKit + ) : base(cmsKit) + { + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json deleted file mode 100644 index b74a907b17..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "culture": "cs", - "texts": { - } -} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pl-PL.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pl-PL.json deleted file mode 100644 index 3ea7b190ee..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pl-PL.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "pl-PL", - "texts": { - - } -} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pt-BR.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pt-BR.json deleted file mode 100644 index 6d746df04c..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/pt-BR.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "pt-BR", - "texts": { - - } -} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sl.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sl.json deleted file mode 100644 index 103359cbc2..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/sl.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "culture": "sl", - "texts": { - } -} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json index ce0d27fd3c..c1664f9122 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json @@ -1,6 +1,18 @@ { "culture": "tr", "texts": { - "PickYourReaction": "Tepkinizi seçin" + "PickYourReaction": "Tepkinizi seçin", + "YourComment": "Yorumunuz", + "YourReply": "Cevabınız", + "Comments": "Yorumlar", + "Send": "Gönder", + "Delete": "Sil", + "Reply": "Cevapla", + "Update": "Güncelle", + "Edit": "Düzenle", + "LoginToAddComment": "Yorum yapmak için giriş yap", + "LoginToReply": "Cevap vermek için giriş yap", + "MessageDeletionConfirmationMessage": "Bu yorum tamamen silinecektir", + "CommentAuthorizationExceptionMessage": "Bu yorumları görebilmek için yetki gerekir." } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/vi.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/vi.json deleted file mode 100644 index 70fc26c87b..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/vi.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "culture": "vi", - "texts": { - } -} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hans.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hans.json deleted file mode 100644 index df56786e15..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hans.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "culture": "zh-Hans", - "texts": { - } -} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hant.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hant.json deleted file mode 100644 index 445e38b82a..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hant.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "culture": "zh-Hant", - "texts": { - } -} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Reactions/UserReactionConsts.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Reactions/UserReactionConsts.cs index be9b10af1b..e89067f869 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Reactions/UserReactionConsts.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Reactions/UserReactionConsts.cs @@ -1,11 +1,13 @@ -namespace Volo.CmsKit.Reactions +using Volo.CmsKit.Entities; + +namespace Volo.CmsKit.Reactions { public static class UserReactionConsts { - public static int EntityTypeLength { get; set; } = 64; + public static int MaxEntityTypeLength { get; set; } = CmsEntityConsts.MaxEntityTypeLength; - public static int EntityIdLength { get; set; } = 64; + public static int MaxEntityIdLength { get; set; } = CmsEntityConsts.MaxEntityIdLength; - public static int ReactionNameLength { get; set; } = 32; + public static int MaxReactionNameLength { get; set; } = 32; } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitOptions.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitOptions.cs index 8e13acca5c..cc69f715ce 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitOptions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitOptions.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using JetBrains.Annotations; +using JetBrains.Annotations; using Volo.CmsKit.Reactions; namespace Volo.CmsKit @@ -9,13 +8,9 @@ namespace Volo.CmsKit [NotNull] public ReactionDefinitionDictionary Reactions { get; } - [NotNull] - public List PublicCommentEntities { get; } - public CmsKitOptions() { Reactions = new ReactionDefinitionDictionary(); - PublicCommentEntities = new List(); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/Comment.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/Comment.cs index a0ee37ccb7..0d2a7e7a18 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/Comment.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/Comment.cs @@ -7,7 +7,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.CmsKit.Comments { - public class Comment: Entity, IAggregateRoot, IHasCreationTime, IMustHaveCreator, IMultiTenant + public class Comment: BasicAggregateRoot, IHasCreationTime, IMustHaveCreator, IMultiTenant { public virtual Guid? TenantId { get; protected set; } @@ -38,16 +38,21 @@ namespace Volo.CmsKit.Comments Guid? tenantId = null) : base(id) { - EntityType = Check.NotNullOrWhiteSpace(entityType, nameof(entityType), CommentConsts.EntityTypeLength); - EntityId = Check.NotNullOrWhiteSpace(entityId, nameof(entityId), CommentConsts.EntityIdLength); + EntityType = Check.NotNullOrWhiteSpace(entityType, nameof(entityType), CommentConsts.MaxEntityTypeLength); + EntityId = Check.NotNullOrWhiteSpace(entityId, nameof(entityId), CommentConsts.MaxEntityIdLength); RepliedCommentId = repliedCommentId; CreatorId = creatorId; TenantId = tenantId; - SetText(text); + SetTextInternal(text); } public virtual void SetText(string text) + { + SetTextInternal(text); + } + + protected virtual void SetTextInternal(string text) { Text = Check.NotNullOrWhiteSpace(text, nameof(text), CommentConsts.MaxTextLength); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentWithAuthor.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentWithAuthorQueryResultItem.cs similarity index 76% rename from modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentWithAuthor.cs rename to modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentWithAuthorQueryResultItem.cs index 3a04a31ee8..a572ec1017 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentWithAuthor.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentWithAuthorQueryResultItem.cs @@ -2,7 +2,7 @@ namespace Volo.CmsKit.Comments { - public class CommentWithAuthor + public class CommentWithAuthorQueryResultItem { public Comment Comment { get; set; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/ICommentRepository.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/ICommentRepository.cs index 8e5bba3427..0c61a22f02 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/ICommentRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/ICommentRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Volo.Abp.Domain.Repositories; @@ -8,8 +9,15 @@ namespace Volo.CmsKit.Comments { public interface ICommentRepository : IBasicRepository { - Task> GetListWithAuthorsAsync( + Task> GetListWithAuthorsAsync( [NotNull] string entityType, - [NotNull] string entityId); + [NotNull] string entityId, + CancellationToken cancellationToken = default + ); + + Task DeleteWithRepliesAsync( + Comment comment, + CancellationToken cancellationToken = default + ); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/DefaultReactionDefinitionStore.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/DefaultReactionDefinitionStore.cs index 4c7d4e095c..84cb15471d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/DefaultReactionDefinitionStore.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/DefaultReactionDefinitionStore.cs @@ -20,7 +20,7 @@ namespace Volo.CmsKit.Reactions return Task.FromResult(Options.Reactions.Values.ToList()); } - public Task GetReactionOrNullAsync(string reactionName, string entityType = null) + public virtual Task GetReactionOrNullAsync(string reactionName, string entityType = null) { return Task.FromResult(Options.Reactions.GetOrDefault(reactionName)); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/IUserReactionRepository.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/IUserReactionRepository.cs index 2c964c70f5..dbbcd0ac29 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/IUserReactionRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/IUserReactionRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Volo.Abp.Domain.Repositories; @@ -12,15 +13,21 @@ namespace Volo.CmsKit.Reactions Guid userId, [NotNull] string entityType, [NotNull] string entityId, - [NotNull] string reactionName); + [NotNull] string reactionName, + CancellationToken cancellationToken = default + ); Task> GetListForUserAsync( Guid userId, [NotNull] string entityType, - [NotNull] string entityId); + [NotNull] string entityId, + CancellationToken cancellationToken = default + ); Task> GetSummariesAsync( [NotNull] string entityType, - [NotNull] string entityId); + [NotNull] string entityId, + CancellationToken cancellationToken = default + ); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/UserReaction.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/UserReaction.cs index fc525e4f65..eff74c7d66 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/UserReaction.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/UserReaction.cs @@ -7,7 +7,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.CmsKit.Reactions { - public class UserReaction : Entity, IAggregateRoot, IHasCreationTime, IMustHaveCreator, IMultiTenant + public class UserReaction : BasicAggregateRoot, IHasCreationTime, IMustHaveCreator, IMultiTenant { public virtual Guid? TenantId { get; protected set; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/CmsUserLookupService.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/CmsUserLookupService.cs index 0771140a10..126d2b9c60 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/CmsUserLookupService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/CmsUserLookupService.cs @@ -19,6 +19,5 @@ namespace Volo.CmsKit.Users { return new CmsUser(externalUser); } - } } diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Comments/EfCoreCommentRepository.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Comments/EfCoreCommentRepository.cs index 3c39386f03..9dec7877f8 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Comments/EfCoreCommentRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Comments/EfCoreCommentRepository.cs @@ -8,6 +8,7 @@ using Volo.Abp; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; using Volo.CmsKit.EntityFrameworkCore; +using Volo.CmsKit.Users; namespace Volo.CmsKit.Comments { @@ -19,39 +20,44 @@ namespace Volo.CmsKit.Comments { } - public async Task> GetListWithAuthorsAsync( + public async Task> GetListWithAuthorsAsync( string entityType, - string entityId) + string entityId, + CancellationToken cancellationToken = default) { Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); Check.NotNullOrWhiteSpace(entityId, nameof(entityId)); var query = from comment in DbSet - join user in DbContext.CmsUsers on comment.CreatorId equals user.Id + join user in DbContext.Set() on comment.CreatorId equals user.Id where entityType == comment.EntityType && entityId == comment.EntityId orderby comment.CreationTime - select new CommentWithAuthor + select new CommentWithAuthorQueryResultItem { Comment = comment, Author = user }; - return await query.ToListAsync(); + return await query.ToListAsync(GetCancellationToken(cancellationToken)); } - public override async Task DeleteAsync(Guid id, bool autoSave = false, CancellationToken cancellationToken = default) + public async Task DeleteWithRepliesAsync( + Comment comment, + CancellationToken cancellationToken = default) { var replies = await DbSet - .Where(x => x.RepliedCommentId == id) + .Where(x => x.RepliedCommentId == comment.Id) .ToListAsync(GetCancellationToken(cancellationToken)); foreach (var reply in replies) { - //TODO: Discuss if it is better to mark it as deleted and show in the ui as "This is deleted" instead of deleting it and replies completely - await base.DeleteAsync(reply.Id, autoSave, GetCancellationToken(cancellationToken)); + await DeleteAsync( + reply, + cancellationToken: GetCancellationToken(cancellationToken) + ); } - await base.DeleteAsync(id, autoSave, GetCancellationToken(cancellationToken)); + await DeleteAsync(comment, cancellationToken: GetCancellationToken(cancellationToken)); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContext.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContext.cs index 8033522f70..65d9b55181 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContext.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContext.cs @@ -1,21 +1,12 @@ using Microsoft.EntityFrameworkCore; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; -using Volo.CmsKit.Comments; -using Volo.CmsKit.Reactions; -using Volo.CmsKit.Users; namespace Volo.CmsKit.EntityFrameworkCore { [ConnectionStringName(CmsKitDbProperties.ConnectionStringName)] public class CmsKitDbContext : AbpDbContext, ICmsKitDbContext { - public DbSet UserReactions { get; set; } - - public DbSet Comments { get; set; } - - public DbSet CmsUsers { get; set; } - public CmsKitDbContext(DbContextOptions options) : base(options) { diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs index cb7a7b78d6..a26b22883e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs @@ -2,10 +2,12 @@ using Microsoft.EntityFrameworkCore; using Volo.Abp; using Volo.Abp.EntityFrameworkCore.Modeling; +using Volo.Abp.GlobalFeatures; using Volo.CmsKit.Comments; using Volo.CmsKit.Reactions; using Volo.CmsKit.Users; using Volo.Abp.Users.EntityFrameworkCore; +using Volo.CmsKit.GlobalFeatures; namespace Volo.CmsKit.EntityFrameworkCore { @@ -30,36 +32,45 @@ namespace Volo.CmsKit.EntityFrameworkCore b.ConfigureByConvention(); b.ConfigureAbpUser(); + + b.HasIndex(x => new {x.TenantId, x.UserName}); + b.HasIndex(x => new {x.TenantId, x.Email}); }); - builder.Entity(b => + if (GlobalFeatureManager.Instance.IsEnabled()) { - b.ToTable(options.TablePrefix + "UserReactions", options.Schema); - b.ConfigureByConvention(); + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "UserReactions", options.Schema); - b.Property(x => x.EntityType).IsRequired().HasMaxLength(UserReactionConsts.EntityTypeLength); - b.Property(x => x.EntityId).IsRequired().HasMaxLength(UserReactionConsts.EntityIdLength); - b.Property(x => x.ReactionName).IsRequired().HasMaxLength(UserReactionConsts.ReactionNameLength); - b.Property(x => x.CreationTime); + b.ConfigureByConvention(); - b.HasIndex(x => new { x.EntityType, x.EntityId }); - b.HasIndex(x => new { x.CreatorId, x.EntityType, x.EntityId, x.ReactionName }); - }); + b.Property(x => x.EntityType).IsRequired().HasMaxLength(UserReactionConsts.MaxEntityTypeLength); + b.Property(x => x.EntityId).IsRequired().HasMaxLength(UserReactionConsts.MaxEntityIdLength); + b.Property(x => x.ReactionName).IsRequired().HasMaxLength(UserReactionConsts.MaxReactionNameLength); + + b.HasIndex(x => new { x.TenantId, x.EntityType, x.EntityId, x.ReactionName }); + b.HasIndex(x => new { x.TenantId, x.CreatorId, x.EntityType, x.EntityId, x.ReactionName }); + }); + } - builder.Entity(b => + if (GlobalFeatureManager.Instance.IsEnabled()) { - b.ToTable(options.TablePrefix + "Comments", options.Schema); - b.ConfigureByConvention(); + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "Comments", options.Schema); - b.Property(x => x.EntityType).IsRequired().HasMaxLength(CommentConsts.EntityTypeLength); - b.Property(x => x.EntityId).IsRequired().HasMaxLength(CommentConsts.EntityIdLength); - b.Property(x => x.Text).IsRequired().HasMaxLength(CommentConsts.MaxTextLength); - b.Property(x => x.RepliedCommentId); - b.Property(x => x.CreationTime); + b.ConfigureByConvention(); - b.HasIndex(x => new { x.EntityType, x.EntityId }); - b.HasIndex(x => new { x.RepliedCommentId }); - }); + b.Property(x => x.EntityType).IsRequired().HasMaxLength(CommentConsts.MaxEntityTypeLength); + b.Property(x => x.EntityId).IsRequired().HasMaxLength(CommentConsts.MaxEntityIdLength); + b.Property(x => x.Text).IsRequired().HasMaxLength(CommentConsts.MaxTextLength); + b.Property(x => x.RepliedCommentId); + + b.HasIndex(x => new { x.TenantId, x.EntityType, x.EntityId }); + b.HasIndex(x => new { x.TenantId, x.RepliedCommentId }); + }); + } } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitEntityFrameworkCoreModule.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitEntityFrameworkCoreModule.cs index 028bc466b2..c528da7d5e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitEntityFrameworkCoreModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitEntityFrameworkCoreModule.cs @@ -19,9 +19,9 @@ namespace Volo.CmsKit.EntityFrameworkCore { context.Services.AddAbpDbContext(options => { + options.AddRepository(); options.AddRepository(); options.AddRepository(); - options.AddRepository(); }); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/ICmsKitDbContext.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/ICmsKitDbContext.cs index e63366d5ae..80bfeb1f5a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/ICmsKitDbContext.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/ICmsKitDbContext.cs @@ -1,19 +1,11 @@ -using Microsoft.EntityFrameworkCore; -using Volo.Abp.Data; +using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; -using Volo.CmsKit.Comments; -using Volo.CmsKit.Reactions; -using Volo.CmsKit.Users; namespace Volo.CmsKit.EntityFrameworkCore { [ConnectionStringName(CmsKitDbProperties.ConnectionStringName)] public interface ICmsKitDbContext : IEfCoreDbContext { - DbSet UserReactions { get; } - DbSet Comments { get; } - - DbSet CmsUsers { get; set; } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Reactions/EfCoreUserReactionRepository.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Reactions/EfCoreUserReactionRepository.cs index 2d0b8ba989..da89de4c74 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Reactions/EfCoreUserReactionRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Reactions/EfCoreUserReactionRepository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Volo.Abp; @@ -22,7 +23,8 @@ namespace Volo.CmsKit.Reactions Guid userId, string entityType, string entityId, - string reactionName) + string reactionName, + CancellationToken cancellationToken = default) { Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); Check.NotNullOrWhiteSpace(entityId, nameof(entityId)); @@ -34,13 +36,14 @@ namespace Volo.CmsKit.Reactions x.EntityType == entityType && x.EntityId == entityId && x.ReactionName == reactionName) - .FirstOrDefaultAsync(); + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } public async Task> GetListForUserAsync( Guid userId, string entityType, - string entityId) + string entityId, + CancellationToken cancellationToken = default) { Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); Check.NotNullOrWhiteSpace(entityId, nameof(entityId)); @@ -50,12 +53,13 @@ namespace Volo.CmsKit.Reactions x.CreatorId == userId && x.EntityType == entityType && x.EntityId == entityId) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); } public async Task> GetSummariesAsync( string entityType, - string entityId) + string entityId, + CancellationToken cancellationToken = default) { Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); Check.NotNullOrWhiteSpace(entityId, nameof(entityId)); @@ -70,7 +74,7 @@ namespace Volo.CmsKit.Reactions ReactionName = g.Key, Count = g.Count() }) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContextExtensions.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContextExtensions.cs index 43b1713506..568e136591 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContextExtensions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContextExtensions.cs @@ -1,6 +1,9 @@ using System; using Volo.Abp; using Volo.Abp.MongoDB; +using Volo.CmsKit.Comments; +using Volo.CmsKit.Reactions; +using Volo.CmsKit.Users; namespace Volo.CmsKit.MongoDB { @@ -17,6 +20,21 @@ namespace Volo.CmsKit.MongoDB ); optionsAction?.Invoke(options); + + builder.Entity(x => + { + x.CollectionName = CmsKitDbProperties.DbTablePrefix + "Users"; + }); + + builder.Entity(x => + { + x.CollectionName = CmsKitDbProperties.DbTablePrefix + "UserReactions"; + }); + + builder.Entity(x => + { + x.CollectionName = CmsKitDbProperties.DbTablePrefix + "Comments"; + }); } } -} \ No newline at end of file +} diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbModule.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbModule.cs index c54a1456aa..736b30ada1 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbModule.cs @@ -22,12 +22,9 @@ namespace Volo.CmsKit.MongoDB { context.Services.AddMongoDbContext(options => { + options.AddRepository(); options.AddRepository(); options.AddRepository(); - options.AddRepository(); - /* Add custom repositories here. Example: - * options.AddRepository(); - */ }); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Comments/MongoCommentRepository.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Comments/MongoCommentRepository.cs index 26a7c09a8c..9564bcf9f8 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Comments/MongoCommentRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Comments/MongoCommentRepository.cs @@ -18,9 +18,10 @@ namespace Volo.CmsKit.MongoDB.Comments { } - public async Task> GetListWithAuthorsAsync( + public async Task> GetListWithAuthorsAsync( string entityType, - string entityId) + string entityId, + CancellationToken cancellationToken = default) { Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); Check.NotNullOrWhiteSpace(entityId, nameof(entityId)); @@ -31,35 +32,43 @@ namespace Volo.CmsKit.MongoDB.Comments orderby comment.CreationTime select user; - var authors = await authorsQuery.ToListAsync(); + var authors = await authorsQuery.ToListAsync(GetCancellationToken(cancellationToken)); var comments = await GetMongoQueryable() .Where(c => c.EntityId == entityId && c.EntityType == entityType) - .OrderBy(c => c.CreationTime).ToListAsync(); + .OrderBy(c => c.CreationTime) + .ToListAsync(GetCancellationToken(cancellationToken)); return comments .Select( comment => - new CommentWithAuthor + new CommentWithAuthorQueryResultItem { Comment = comment, Author = authors.FirstOrDefault(a => a.Id == comment.CreatorId) }).ToList(); } - public override async Task DeleteAsync(Guid id, bool autoSave = false, CancellationToken cancellationToken = default) + public async Task DeleteWithRepliesAsync( + Comment comment, + CancellationToken cancellationToken = default) { var replies = await GetMongoQueryable() - .Where(x => x.RepliedCommentId == id) + .Where(x => x.RepliedCommentId == comment.Id) .ToListAsync(GetCancellationToken(cancellationToken)); foreach (var reply in replies) { - //TODO: Discuss if it is better to mark it as deleted and show in the ui as "This is deleted" instead of deleting it and replies completely - await base.DeleteAsync(reply.Id, autoSave, GetCancellationToken(cancellationToken)); + await base.DeleteAsync( + reply, + cancellationToken: GetCancellationToken(cancellationToken) + ); } - await base.DeleteAsync(id, autoSave, GetCancellationToken(cancellationToken)); + await base.DeleteAsync( + comment, + cancellationToken: GetCancellationToken(cancellationToken) + ); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Reactions/MongoUserReactionRepository.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Reactions/MongoUserReactionRepository.cs index ae3b28766a..e3a09c4514 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Reactions/MongoUserReactionRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Reactions/MongoUserReactionRepository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using MongoDB.Driver; using MongoDB.Driver.Linq; @@ -21,7 +22,8 @@ namespace Volo.CmsKit.MongoDB.Reactions Guid userId, string entityType, string entityId, - string reactionName) + string reactionName, + CancellationToken cancellationToken = default) { Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); Check.NotNullOrWhiteSpace(entityId, nameof(entityId)); @@ -33,13 +35,14 @@ namespace Volo.CmsKit.MongoDB.Reactions x.EntityType == entityType && x.EntityId == entityId && x.ReactionName == reactionName) - .FirstOrDefaultAsync(); + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } public async Task> GetListForUserAsync( Guid userId, string entityType, - string entityId) + string entityId, + CancellationToken cancellationToken = default) { Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); Check.NotNullOrWhiteSpace(entityId, nameof(entityId)); @@ -49,12 +52,13 @@ namespace Volo.CmsKit.MongoDB.Reactions x.CreatorId == userId && x.EntityType == entityType && x.EntityId == entityId) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); } public async Task> GetSummariesAsync( string entityType, - string entityId) + string entityId, + CancellationToken cancellationToken = default) { Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); Check.NotNullOrWhiteSpace(entityId, nameof(entityId)); @@ -69,7 +73,7 @@ namespace Volo.CmsKit.MongoDB.Reactions ReactionName = g.Key, Count = g.Count() }) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Users/MongoCmsUserRepository.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Users/MongoCmsUserRepository.cs index ed2691a5a8..22012e48c0 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Users/MongoCmsUserRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Users/MongoCmsUserRepository.cs @@ -10,6 +10,5 @@ namespace Volo.CmsKit.MongoDB.Users : base(dbContextProvider) { } - } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CmsUserDto.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CmsUserDto.cs index b246cbc915..060117c201 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CmsUserDto.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CmsUserDto.cs @@ -8,8 +8,6 @@ namespace Volo.CmsKit.Public.Comments public string UserName { get; set; } - public string Email { get; set; } - public string Name { get; set; } public string Surname { get; set; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CommentDto.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CommentDto.cs index f7bdf27d13..106ca46802 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CommentDto.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CommentDto.cs @@ -18,6 +18,6 @@ namespace Volo.CmsKit.Public.Comments public DateTime CreationTime { get; set; } - public CmsUserDto Author { get; set; } + public CmsUserDto Author { get; set; } //TODO: Should only have AuthorId for the basic dto. see https://docs.abp.io/en/abp/latest/Best-Practices/Application-Services } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs index ebbc41898e..c489721466 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs @@ -7,14 +7,6 @@ namespace Volo.CmsKit.Public.Comments { public class CreateCommentInput { - [Required] - [DynamicStringLength(typeof(CommentConsts), nameof(CommentConsts.EntityTypeLength))] - public string EntityType { get; set; } - - [Required] - [DynamicStringLength(typeof(CommentConsts), nameof(CommentConsts.EntityIdLength))] - public string EntityId { get; set; } - [Required] [DynamicStringLength(typeof(CommentConsts), nameof(CommentConsts.MaxTextLength))] public string Text { get; set; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/ICommentPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/ICommentPublicAppService.cs index 8f13c7fb05..8ac8c92988 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/ICommentPublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/ICommentPublicAppService.cs @@ -7,9 +7,9 @@ namespace Volo.CmsKit.Public.Comments { public interface ICommentPublicAppService : IApplicationService { - Task> GetAllForEntityAsync(string entityType, string entityId); + Task> GetListAsync(string entityType, string entityId); - Task CreateAsync(CreateCommentInput input); + Task CreateAsync(string entityType, string entityId, CreateCommentInput input); Task UpdateAsync(Guid id, UpdateCommentInput input); diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Reactions/CreateReactionDto.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Reactions/CreateReactionDto.cs deleted file mode 100644 index 8612ba6762..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Reactions/CreateReactionDto.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Volo.CmsKit.Public.Reactions -{ - public class CreateReactionDto - { - public string EntityType { get; set; } - - public string EntityId { get; set; } - - public string ReactionName { get; set; } - } -} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Reactions/DeleteReactionDto.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Reactions/DeleteReactionDto.cs deleted file mode 100644 index 8ce4dd6808..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Reactions/DeleteReactionDto.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Volo.CmsKit.Public.Reactions -{ - public class DeleteReactionDto - { - public string EntityType { get; set; } - - public string EntityId { get; set; } - - public string ReactionName { get; set; } - } -} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Reactions/IReactionPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Reactions/IReactionPublicAppService.cs index 3058148afd..8a32d7cc11 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Reactions/IReactionPublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Reactions/IReactionPublicAppService.cs @@ -8,8 +8,8 @@ namespace Volo.CmsKit.Public.Reactions { Task> GetForSelectionAsync(string entityType, string entityId); - Task CreateAsync(CreateReactionDto input); + Task CreateAsync(string entityType, string entityId, string reaction); - Task DeleteAsync(DeleteReactionDto input); + Task DeleteAsync(string entityType, string entityId, string reaction); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/CmsKitPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/CmsKitPublicAppServiceBase.cs similarity index 53% rename from modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/CmsKitPublicAppService.cs rename to modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/CmsKitPublicAppServiceBase.cs index afdc42fd1a..eb2c185a6d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/CmsKitPublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/CmsKitPublicAppServiceBase.cs @@ -1,8 +1,8 @@ namespace Volo.CmsKit.Public { - public abstract class CmsKitPublicAppService : CmsKitAppService + public abstract class CmsKitPublicAppServiceBase : CmsKitAppServiceBase { - protected CmsKitPublicAppService() + protected CmsKitPublicAppServiceBase() { ObjectMapperContext = typeof(CmsKitPublicApplicationModule); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs index 895d8c5948..88bcef9a1e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs @@ -7,8 +7,6 @@ using Microsoft.Extensions.Options; using Volo.Abp; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; -using Volo.Abp.Authorization; -using Volo.Abp.MultiTenancy; using Volo.Abp.Users; using Volo.CmsKit.Comments; using Volo.CmsKit.Users; @@ -31,36 +29,32 @@ namespace Volo.CmsKit.Public.Comments CmsUserLookupService = cmsUserLookupService; } - public virtual async Task> GetAllForEntityAsync(string entityType, string entityId) + public virtual async Task> GetListAsync(string entityType, string entityId) { - CheckAuthorizationAsync(entityType); - - var commentsWithAuthor = await CommentRepository.GetListWithAuthorsAsync(entityType, entityId); + var commentsWithAuthor = await CommentRepository + .GetListWithAuthorsAsync(entityType, entityId); return new ListResultDto( ConvertCommentsToNestedStructure(commentsWithAuthor) - ); + ); } [Authorize] - public virtual async Task CreateAsync(CreateCommentInput input) + public virtual async Task CreateAsync(string entityType, string entityId, CreateCommentInput input) { - var user = await CmsUserLookupService.FindByIdAsync(CurrentUser.GetId()); - - if (user == null) - { - throw new BusinessException(message: "User Not found!"); - } - - var comment = await CommentRepository.InsertAsync(new Comment( - GuidGenerator.Create(), - input.EntityType, - input.EntityId, - input.Text, - input.RepliedCommentId, - user.Id, - CurrentTenant.Id - )); + var user = await CmsUserLookupService.GetByIdAsync(CurrentUser.GetId()); + + var comment = await CommentRepository.InsertAsync( + new Comment( + GuidGenerator.Create(), + entityType, + entityId, + input.Text, + input.RepliedCommentId, + user.Id, + CurrentTenant.Id + ) + ); return ObjectMapper.Map(comment); } @@ -72,7 +66,7 @@ namespace Volo.CmsKit.Public.Comments if (comment.CreatorId != CurrentUser.GetId()) { - throw new BusinessException(); + throw new BusinessException(); //TODO: AbpAuthorizationException! } comment.SetText(input.Text); @@ -89,14 +83,16 @@ namespace Volo.CmsKit.Public.Comments if (comment.CreatorId != CurrentUser.GetId()) { - throw new BusinessException(); + throw new BusinessException(); //TODO: AbpAuthorizationException! } - await CommentRepository.DeleteAsync(id); + await CommentRepository.DeleteWithRepliesAsync(comment); } - private List ConvertCommentsToNestedStructure(List comments) + private List ConvertCommentsToNestedStructure(List comments) { + //TODO: I think this method can be optimized if you use dictionaries instead of straight search + var parentComments = comments .Where(c=> c.Comment.RepliedCommentId == null) .Select(c=> ObjectMapper.Map(c.Comment)) @@ -120,25 +116,7 @@ namespace Volo.CmsKit.Public.Comments return parentComments; } - private async Task CheckAuthorizationAsync(string entityType) - { - if (await IsPublicEntity(entityType)) - { - return; - } - - if (!CurrentUser.IsAuthenticated) - { - throw new AbpAuthorizationException(L["CommentAuthorizationExceptionMessage"]); - } - } - - private async Task IsPublicEntity(string entityType) - { - return CmsKitOptions.PublicCommentEntities.Contains(entityType); - } - - private CmsUserDto GetAuthorAsDtoFromCommentList(List comments, Guid commentId) + private CmsUserDto GetAuthorAsDtoFromCommentList(List comments, Guid commentId) { return ObjectMapper.Map(comments.Single(c => c.Comment.Id == commentId).Author); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs index 92b6cc0895..e0b64bb0e8 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs @@ -10,13 +10,14 @@ namespace Volo.CmsKit.Public { public PublicApplicationAutoMapperProfile() { - /* You can configure your AutoMapper mapping configuration here. - * Alternatively, you can split your mapping configurations - * into multiple profile classes for a better organization. */ - CreateMap(); - CreateMap().Ignore(x=> x.Author); - CreateMap().Ignore(x=> x.Replies).Ignore(x=> x.Author); + + CreateMap() + .Ignore(x=> x.Author); + + CreateMap() + .Ignore(x=> x.Replies) + .Ignore(x=> x.Author); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Reactions/ReactionPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Reactions/ReactionPublicAppService.cs index 6a1431f065..44135d867e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Reactions/ReactionPublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Reactions/ReactionPublicAppService.cs @@ -8,8 +8,7 @@ using Volo.CmsKit.Reactions; namespace Volo.CmsKit.Public.Reactions { - //TODO: Authorization - public class ReactionPublicAppService : CmsKitPublicAppService, IReactionPublicAppService + public class ReactionPublicAppService : CmsKitPublicAppServiceBase, IReactionPublicAppService { protected IReactionDefinitionStore ReactionDefinitionStore { get; } @@ -31,13 +30,14 @@ namespace Volo.CmsKit.Public.Reactions { var summaries = await ReactionManager.GetSummariesAsync(entityType, entityId); - var userReactions = CurrentUser.IsAuthenticated ? - (await UserReactionRepository - .GetListForUserAsync( - CurrentUser.GetId(), - entityType, - entityId - )).ToDictionary(x => x.ReactionName, x => x) : null; + var userReactionsOrNull = CurrentUser.IsAuthenticated + ? (await UserReactionRepository + .GetListForUserAsync( + CurrentUser.GetId(), + entityType, + entityId + )).ToDictionary(x => x.ReactionName, x => x) + : null; var reactionWithSelectionDtos = new List(); @@ -48,7 +48,7 @@ namespace Volo.CmsKit.Public.Reactions { Reaction = ConvertToReactionDto(summary.Reaction), Count = summary.Count, - IsSelectedByCurrentUser = userReactions?.ContainsKey(summary.Reaction.Name) ?? false + IsSelectedByCurrentUser = userReactionsOrNull?.ContainsKey(summary.Reaction.Name) ?? false } ); } @@ -57,24 +57,24 @@ namespace Volo.CmsKit.Public.Reactions } [Authorize] - public virtual async Task CreateAsync(CreateReactionDto input) + public virtual async Task CreateAsync(string entityType, string entityId, string reaction) { await ReactionManager.CreateAsync( CurrentUser.GetId(), - input.EntityType, - input.EntityId, - input.ReactionName + entityType, + entityId, + reaction ); } [Authorize] - public virtual async Task DeleteAsync(DeleteReactionDto input) + public virtual async Task DeleteAsync(string entityType, string entityId, string reaction) { await ReactionManager.DeleteAsync( CurrentUser.GetId(), - input.EntityType, - input.EntityId, - input.ReactionName + entityType, + entityId, + reaction ); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/CmsKitPublicControllerBase.cs b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/CmsKitPublicControllerBase.cs index 79bb1b63ba..49aa98c134 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/CmsKitPublicControllerBase.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/CmsKitPublicControllerBase.cs @@ -4,5 +4,6 @@ namespace Volo.CmsKit.Public { public abstract class CmsKitPublicControllerBase : CmsKitControllerBase { + } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Comments/CommentPublicController.cs b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Comments/CommentPublicController.cs index 22c121ce58..1b5b2cc64d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Comments/CommentPublicController.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Comments/CommentPublicController.cs @@ -3,9 +3,12 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp; using Volo.Abp.Application.Dtos; +using Volo.Abp.GlobalFeatures; +using Volo.CmsKit.GlobalFeatures; namespace Volo.CmsKit.Public.Comments { + [RequiresGlobalFeature(typeof(CommentsFeature))] [RemoteService(Name = CmsKitPublicRemoteServiceConsts.RemoteServiceName)] [Area("cms-kit")] [Route("api/cms-kit-public/comments")] @@ -20,18 +23,19 @@ namespace Volo.CmsKit.Public.Comments [HttpGet] [Route("{entityType}/{entityId}")] - public Task> GetAllForEntityAsync(string entityType, string entityId) + public Task> GetListAsync(string entityType, string entityId) { - return CommentPublicAppService.GetAllForEntityAsync(entityType, entityId); + return CommentPublicAppService.GetListAsync(entityType, entityId); } [HttpPost] - public Task CreateAsync(CreateCommentInput input) + [Route("{entityType}/{entityId}")] + public Task CreateAsync(string entityType, string entityId, CreateCommentInput input) { - return CommentPublicAppService.CreateAsync(input); + return CommentPublicAppService.CreateAsync(entityType, entityId, input); } - [HttpPost] + [HttpPut] [Route("{id}")] public Task UpdateAsync(Guid id, UpdateCommentInput input) { @@ -39,7 +43,7 @@ namespace Volo.CmsKit.Public.Comments } [HttpDelete] - [Route("update")] + [Route("{id}")] public Task DeleteAsync(Guid id) { return CommentPublicAppService.DeleteAsync(id); diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Reactions/ReactionPublicController.cs b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Reactions/ReactionPublicController.cs index cbab4a649f..65170dc2bd 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Reactions/ReactionPublicController.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Reactions/ReactionPublicController.cs @@ -2,9 +2,12 @@ using Microsoft.AspNetCore.Mvc; using Volo.Abp; using Volo.Abp.Application.Dtos; +using Volo.Abp.GlobalFeatures; +using Volo.CmsKit.GlobalFeatures; namespace Volo.CmsKit.Public.Reactions { + [RequiresGlobalFeature(typeof(ReactionsFeature))] [RemoteService(Name = CmsKitPublicRemoteServiceConsts.RemoteServiceName)] [Area("cms-kit")] [Route("api/cms-kit-public/reactions")] @@ -25,15 +28,17 @@ namespace Volo.CmsKit.Public.Reactions } [HttpPut] - public virtual Task CreateAsync(CreateReactionDto input) + [Route("{entityType}/{entityId}/{reaction}")] + public virtual Task CreateAsync(string entityType, string entityId, string reaction) { - return ReactionPublicAppService.CreateAsync(input); + return ReactionPublicAppService.CreateAsync(entityType, entityId, reaction); } [HttpDelete] - public virtual Task DeleteAsync(DeleteReactionDto input) + [Route("{entityType}/{entityId}/{reaction}")] + public virtual Task DeleteAsync(string entityType, string entityId, string reaction) { - return ReactionPublicAppService.DeleteAsync(input); + return ReactionPublicAppService.DeleteAsync(entityType, entityId, reaction); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebAutoMapperProfile.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebAutoMapperProfile.cs index da60048298..d168c4fc75 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebAutoMapperProfile.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebAutoMapperProfile.cs @@ -6,9 +6,7 @@ namespace Volo.CmsKit.Public.Web { public CmsKitPublicWebAutoMapperProfile() { - /* You can configure your AutoMapper mapping configuration here. - * Alternatively, you can split your mapping configurations - * into multiple profile classes for a better organization. */ + } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs index 2b4a2e8fce..862213ab7a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs @@ -21,7 +21,12 @@ namespace Volo.CmsKit.Public.Web { context.Services.PreConfigure(options => { - options.AddAssemblyResource(typeof(CmsKitResource), typeof(CmsKitPublicWebModule).Assembly); + options.AddAssemblyResource( + typeof(CmsKitResource), + typeof(CmsKitPublicWebModule).Assembly, + typeof(CmsKitPublicApplicationContractsModule).Assembly, + typeof(CmsKitCommonApplicationContractsModule).Assembly + ); }); PreConfigure(mvcBuilder => @@ -43,6 +48,7 @@ namespace Volo.CmsKit.Public.Web }); context.Services.AddAutoMapperObjectMapper(); + Configure(options => { options.AddMaps(validate: true); @@ -50,7 +56,7 @@ namespace Volo.CmsKit.Public.Web Configure(options => { - //Configure authorization. + //... }); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicWidgetsController.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicWidgetsController.cs index 29d6e27d1f..c130ebefca 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicWidgetsController.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicWidgetsController.cs @@ -5,17 +5,16 @@ using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.ReactionSelection; namespace Volo.CmsKit.Public.Web.Controllers { - //TODO: Consider to move to an area, but also consider to not have the same prefix with API Controllers which can be problem in case of a tiered architecture public class CmsKitPublicWidgetsController : CmsKitPublicControllerBase { - public async Task ReactionSelection(string entityType, string entityId) + public Task ReactionSelection(string entityType, string entityId) { - return ViewComponent(typeof(ReactionSelectionViewComponent), new {entityType, entityId}); + return Task.FromResult((IActionResult)ViewComponent(typeof(ReactionSelectionViewComponent), new {entityType, entityId})); } - public async Task Commenting(string entityType, string entityId) + public Task Commenting(string entityType, string entityId) { - return ViewComponent(typeof(CommentingViewComponent), new {entityType, entityId}); + return Task.FromResult((IActionResult)ViewComponent(typeof(CommentingViewComponent), new {entityType, entityId})); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Menus/CmsKitPublicMenus.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Menus/CmsKitPublicMenus.cs index 5b8e026122..eee362a20b 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Menus/CmsKitPublicMenus.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Menus/CmsKitPublicMenus.cs @@ -6,6 +6,5 @@ //Add your menu items here... //public const string Home = Prefix + ".MyNewMenuItem"; - } -} \ No newline at end of file +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs index ceafd07134..e8d6e73e9a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs @@ -1,10 +1,13 @@ -using System.Collections.Generic; -using System.Linq; +using System; +using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI; using Volo.Abp.AspNetCore.Mvc.UI.Widgets; using Volo.CmsKit.Public.Comments; +using Volo.CmsKit.Web; namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting { @@ -17,23 +20,31 @@ namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting public class CommentingViewComponent : AbpViewComponent { public ICommentPublicAppService CommentPublicAppService { get; } + public AbpMvcUiOptions AbpMvcUiOptions { get; } public CommentingViewComponent( - ICommentPublicAppService commentPublicAppService) + ICommentPublicAppService commentPublicAppService, + IOptions options) { CommentPublicAppService = commentPublicAppService; + AbpMvcUiOptions = options.Value; } - public virtual async Task InvokeAsync(string entityType, string entityId, string loginUrl = null) + public virtual async Task InvokeAsync( + string entityType, + string entityId) { - var result = await CommentPublicAppService.GetAllForEntityAsync(entityType, entityId); + var result = await CommentPublicAppService + .GetListAsync(entityType, entityId); + + var loginUrl = $"{AbpMvcUiOptions.LoginUrl}?returnUrl={HttpContext.Request.Path.ToString()}&returnUrlHash=#cms-comment_{entityType}_{entityId}"; var viewModel = new CommentingViewModel { EntityId = entityId, EntityType = entityType, LoginUrl = loginUrl, - Comments = result.Items.ToList() + Comments = result.Items }; return View("~/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml", viewModel); @@ -47,7 +58,7 @@ namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting public string LoginUrl { get; set; } - public List Comments { get; set; } + public IReadOnlyList Comments { get; set; } } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml index 743f4f7d9c..5246f9a0f1 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml @@ -1,7 +1,6 @@ @using Microsoft.AspNetCore.Html @using Microsoft.AspNetCore.Mvc.Localization @using Volo.Abp.Users -@using Volo.CmsKit.Comments @using Volo.CmsKit.Localization @using Volo.CmsKit.Public.Comments @inject ICurrentUser CurrentUser @@ -55,7 +54,7 @@ } else { - @L["LoginToReply"] + @L["LoginToReply"] } } @if (authorId == CurrentUser.Id) diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js index 4fb29abb51..2950f8239b 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js @@ -122,10 +122,12 @@ e.preventDefault(); var formAsObject = $form.serializeFormToObject(); volo.cmsKit.public.comments.commentPublic.create( - $.extend(getFilters(), { + $commentArea.attr('data-entity-type'), + $commentArea.attr('data-entity-id'), + { repliedCommentId: formAsObject.repliedCommentId, text: formAsObject.commentText - }) + } ).then(function () { widgetManager.refresh($widget); }); diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/ReactionSelection/default.js b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/ReactionSelection/default.js index 38e5da93e5..3214d3af6b 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/ReactionSelection/default.js +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/ReactionSelection/default.js @@ -33,9 +33,9 @@ $icon.click(function () { var methodName = $icon.hasClass('cms-reaction-icon-selected') ? 'delete' : 'create'; volo.cmsKit.public.reactions.reactionPublic[methodName]( - $.extend(getFilters(), { - reactionName: reactionName - }) + $reactionArea.attr('data-entity-type'), + $reactionArea.attr('data-entity-id'), + reactionName ).then(function () { $selectIcon.popover('hide'); widgetManager.refresh($widget); diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/PublicPageModel.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/PublicPageModel.cs index 0ea29457e9..bbc6de3dfe 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/PublicPageModel.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/PublicPageModel.cs @@ -3,14 +3,12 @@ using Volo.CmsKit.Localization; namespace Volo.CmsKit.Public.Web.Pages { - /* Inherit your PageModel classes from this class. - */ - public abstract class PublicPageModel : AbpPageModel + public abstract class CmsKitPublicPageModelBase : AbpPageModel { - protected PublicPageModel() + protected CmsKitPublicPageModelBase() { LocalizationResourceType = typeof(CmsKitResource); ObjectMapperContext = typeof(CmsKitPublicWebModule); } } -} \ No newline at end of file +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj index 102e2038d1..6d2f8ff303 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj @@ -23,6 +23,10 @@ + + + + diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Comments/CommentPublicAppService_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Comments/CommentPublicAppService_Tests.cs index 9568f79c6f..ada9805a51 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Comments/CommentPublicAppService_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Comments/CommentPublicAppService_Tests.cs @@ -1,11 +1,8 @@ -using System; -using System.Linq; +using System.Linq; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using NSubstitute; using Shouldly; -using Volo.Abp.MultiTenancy; using Volo.Abp.Users; using Volo.CmsKit.Public.Comments; using Xunit; @@ -33,7 +30,7 @@ namespace Volo.CmsKit.Comments [Fact] public async Task GetAllForEntityAsync() { - var list = await _commentAppService.GetAllForEntityAsync(_cmsKitTestData.EntityType1, _cmsKitTestData.EntityId1); + var list = await _commentAppService.GetListAsync(_cmsKitTestData.EntityType1, _cmsKitTestData.EntityId1); list.Items.Count.ShouldBe(2); list.Items.First().Replies.Count.ShouldBe(2); @@ -44,17 +41,19 @@ namespace Volo.CmsKit.Comments { _currentUser.Id.Returns(_cmsKitTestData.User2Id); - var newComment = await _commentAppService.CreateAsync(new CreateCommentInput - { - EntityId = _cmsKitTestData.EntityId1, - EntityType = _cmsKitTestData.EntityType1, - RepliedCommentId = null, - Text = "newComment" - }); + var newComment = await _commentAppService.CreateAsync( + _cmsKitTestData.EntityType1, + _cmsKitTestData.EntityId1, + new CreateCommentInput + { + RepliedCommentId = null, + Text = "newComment" + } + ); UsingDbContext(context => { - var comments = context.Comments.Where(x => + var comments = context.Set().Where(x => x.EntityId == _cmsKitTestData.EntityId1 && x.EntityType == _cmsKitTestData.EntityType1).ToList(); comments @@ -75,7 +74,7 @@ namespace Volo.CmsKit.Comments UsingDbContext(context => { - var comment = context.Comments.Single(x => + var comment = context.Set().Single(x => x.Id == _cmsKitTestData.CommentWithChildId); comment.Text.ShouldBe("I'm Updated"); @@ -91,7 +90,7 @@ namespace Volo.CmsKit.Comments UsingDbContext(context => { - var comment = context.Comments.FirstOrDefault(x => + var comment = context.Set().FirstOrDefault(x => x.Id == _cmsKitTestData.CommentWithChildId); comment.ShouldBeNull(); diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Reactions/ReactionPublicAppService_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Reactions/ReactionPublicAppService_Tests.cs index d03712ea07..3872861ef7 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Reactions/ReactionPublicAppService_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Reactions/ReactionPublicAppService_Tests.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using NSubstitute; @@ -43,6 +40,7 @@ namespace Volo.CmsKit.Reactions reactions.Items. First(r=>r.Reaction.Name == StandardReactions.Rocket).IsSelectedByCurrentUser.ShouldBeTrue(); + reactions.Items. First(r=>r.Reaction.Name == StandardReactions.Rocket).Count.ShouldBe(1); @@ -59,16 +57,15 @@ namespace Volo.CmsKit.Reactions { _currentUser.Id.Returns(_cmsKitTestData.User1Id); - await _reactionPublicAppService.CreateAsync(new CreateReactionDto - { - EntityType = _cmsKitTestData.EntityType2, - EntityId = _cmsKitTestData.EntityId2, - ReactionName = StandardReactions.Hooray - }); + await _reactionPublicAppService.CreateAsync( + _cmsKitTestData.EntityType2, + _cmsKitTestData.EntityId2, + StandardReactions.Hooray + ); UsingDbContext(context => { - var reaction = context.UserReactions.FirstOrDefault(x => + var reaction = context.Set().FirstOrDefault(x => x.CreatorId == _cmsKitTestData.User1Id && x.ReactionName == StandardReactions.Hooray && x.EntityId == _cmsKitTestData.EntityId2 && @@ -83,16 +80,15 @@ namespace Volo.CmsKit.Reactions { _currentUser.Id.Returns(_cmsKitTestData.User1Id); - await _reactionPublicAppService.DeleteAsync(new DeleteReactionDto - { - EntityType = _cmsKitTestData.EntityType1, - EntityId = _cmsKitTestData.EntityId1, - ReactionName = StandardReactions.Confused - }); + await _reactionPublicAppService.DeleteAsync( + _cmsKitTestData.EntityType1, + _cmsKitTestData.EntityId1, + StandardReactions.Confused + ); UsingDbContext(context => { - var reaction = context.UserReactions.FirstOrDefault(x => + var reaction = context.Set().FirstOrDefault(x => x.CreatorId == _cmsKitTestData.User1Id && x.ReactionName == StandardReactions.Confused && x.EntityId == _cmsKitTestData.EntityId1 && diff --git a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Reactions/ReactionManager_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Reactions/ReactionManager_Tests.cs index 6f4fe2425d..453d393bb8 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Reactions/ReactionManager_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Reactions/ReactionManager_Tests.cs @@ -69,7 +69,7 @@ namespace Volo.CmsKit.Reactions UsingDbContext(context => { - var reaction = context.UserReactions.FirstOrDefault(x => + var reaction = context.Set().FirstOrDefault(x => x.CreatorId == _cmsKitTestData.User1Id && x.ReactionName == StandardReactions.Confused && x.EntityId == _cmsKitTestData.EntityId1 && diff --git a/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Comments/CommentRepository_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Comments/CommentRepository_Tests.cs index cf2e936ca8..06a8007683 100644 --- a/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Comments/CommentRepository_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Comments/CommentRepository_Tests.cs @@ -1,7 +1,9 @@ using Volo.CmsKit.Comments; +using Xunit; namespace Volo.CmsKit.MongoDB.Comments { + [Collection(MongoTestCollection.Name)] public class CommentRepository_Tests : CommentRepository_Tests { diff --git a/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Reactions/UserReactionRepository_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Reactions/UserReactionRepository_Tests.cs index 452ee211c5..16740a5803 100644 --- a/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Reactions/UserReactionRepository_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Reactions/UserReactionRepository_Tests.cs @@ -1,7 +1,9 @@ using Volo.CmsKit.Reactions; +using Xunit; namespace Volo.CmsKit.MongoDB.Reactions { + [Collection(MongoTestCollection.Name)] public class UserReactionRepository_Tests : UserReactionRepository_Tests { } diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBaseModule.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBaseModule.cs index ae1f0f7296..8e5049f55e 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBaseModule.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBaseModule.cs @@ -3,6 +3,7 @@ using Volo.Abp; using Volo.Abp.Authorization; using Volo.Abp.Autofac; using Volo.Abp.Data; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Modularity; using Volo.Abp.Threading; @@ -16,8 +17,15 @@ namespace Volo.CmsKit )] public class CmsKitTestBaseModule : AbpModule { + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + public override void ConfigureServices(ServiceConfigurationContext context) { + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.CmsKit().EnableAll(); + }); + context.Services.AddAlwaysAllowAuthorization(); } diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/Comments/CommentRepository_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/Comments/CommentRepository_Tests.cs index afb7652edf..80b3052798 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/Comments/CommentRepository_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/Comments/CommentRepository_Tests.cs @@ -30,9 +30,10 @@ namespace Volo.CmsKit.Comments } [Fact] - public async Task DeleteAsync() + public async Task DeleteWithRepliesAsync() { - await _commentRepository.DeleteAsync(_cmsKitTestData.CommentWithChildId); + var comment = await _commentRepository.GetAsync(_cmsKitTestData.CommentWithChildId); + await _commentRepository.DeleteWithRepliesAsync(comment); var list = await _commentRepository.GetListAsync(); diff --git a/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj b/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj index 2e06315603..d2f04bb1f5 100644 --- a/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj +++ b/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj @@ -19,7 +19,7 @@ - + diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj b/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj index 8e6805cf0a..47e5560f8f 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj +++ b/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj @@ -38,6 +38,10 @@ + + + + diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs index b525aa65d9..8e0a51324a 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs @@ -26,7 +26,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic _options = options.Value; } - public async Task CreateIndexIfNeededAsync(CancellationToken cancellationToken = default) + public virtual async Task CreateIndexIfNeededAsync(CancellationToken cancellationToken = default) { ValidateElasticSearchEnabled(); @@ -54,7 +54,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic } } - public async Task AddOrUpdateAsync(Document document, CancellationToken cancellationToken = default) + public virtual async Task AddOrUpdateAsync(Document document, CancellationToken cancellationToken = default) { ValidateElasticSearchEnabled(); @@ -67,13 +67,13 @@ namespace Volo.Docs.Documents.FullSearch.Elastic var esDocument = new EsDocument { - Id = document.Id, - ProjectId = document.ProjectId, + Id = NormalizeField(document.Id), + ProjectId = NormalizeField(document.ProjectId), Name = document.Name, FileName = document.FileName, Content = document.Content, - LanguageCode = ConvertLanguageCode(document.LanguageCode), - Version = ConvertVersion(document.Version) + LanguageCode = NormalizeField(document.LanguageCode), + Version = NormalizeField(document.Version) }; if (!existsResponse.Exists) @@ -89,7 +89,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic } - public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + public virtual async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) { ValidateElasticSearchEnabled(); @@ -97,7 +97,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic .DeleteAsync(DocumentPath.Id(id), x => x.Index(_options.IndexName), cancellationToken)); } - public async Task DeleteAllAsync(CancellationToken cancellationToken = default) + public virtual async Task DeleteAllAsync(CancellationToken cancellationToken = default) { ValidateElasticSearchEnabled(); @@ -110,7 +110,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic .DeleteByQueryAsync(request, cancellationToken)); } - public async Task DeleteAllByProjectIdAsync(Guid projectId, CancellationToken cancellationToken = default) + public virtual async Task DeleteAllByProjectIdAsync(Guid projectId, CancellationToken cancellationToken = default) { ValidateElasticSearchEnabled(); @@ -127,7 +127,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic new TermQuery { Field = "projectId", - Value = projectId + Value = NormalizeField(projectId) } } } @@ -139,7 +139,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic .DeleteByQueryAsync(request, cancellationToken)); } - public async Task> SearchAsync(string context, Guid projectId, string languageCode, + public virtual async Task> SearchAsync(string context, Guid projectId, string languageCode, string version, int? skipCount = null, int? maxResultCount = null, CancellationToken cancellationToken = default) { @@ -168,17 +168,17 @@ namespace Volo.Docs.Documents.FullSearch.Elastic new TermQuery { Field = "projectId", - Value = projectId + Value = NormalizeField(projectId) }, new TermQuery { Field = "version", - Value = ConvertVersion(version) + Value = NormalizeField(version) }, new TermQuery { Field = "languageCode", - Value = ConvertLanguageCode(languageCode) + Value = NormalizeField(languageCode) } } } @@ -216,7 +216,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic return docs; } - protected void HandleError(IElasticsearchResponse response) + protected virtual void HandleError(IElasticsearchResponse response) { if (!response.ApiCall.Success) { @@ -225,7 +225,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic } } - protected void ValidateElasticSearchEnabled() + protected virtual void ValidateElasticSearchEnabled() { if (!_options.Enable) { @@ -233,14 +233,14 @@ namespace Volo.Docs.Documents.FullSearch.Elastic } } - protected string ConvertLanguageCode(string languageCode) + protected virtual string NormalizeField(Guid field) { - return languageCode.Replace("-", "").ToLower(); + return NormalizeField(field.ToString("N")); } - protected string ConvertVersion(string version) + protected virtual string NormalizeField(string field) { - return version.Replace("-", "").ToLower(); + return field.Replace("-", "").ToLower(); } } } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/EsDocument.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/EsDocument.cs index df78744361..be930d0ee1 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/EsDocument.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/EsDocument.cs @@ -10,9 +10,9 @@ namespace Volo.Docs.Documents.FullSearch.Elastic Highlight = new List(); } - public Guid Id { get; set; } + public string Id { get; set; } - public Guid ProjectId { get; set; } + public string ProjectId { get; set; } public string Name { get; set; } @@ -26,4 +26,4 @@ namespace Volo.Docs.Documents.FullSearch.Elastic public List Highlight { get; set; } } -} \ No newline at end of file +} diff --git a/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj b/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj index cde0af6a76..9342831414 100644 --- a/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj +++ b/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj @@ -38,6 +38,10 @@ + + + +
diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Volo.Abp.FeatureManagement.Web.csproj b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Volo.Abp.FeatureManagement.Web.csproj index 735678e332..f45554d32c 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Volo.Abp.FeatureManagement.Web.csproj +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Volo.Abp.FeatureManagement.Web.csproj @@ -15,8 +15,12 @@ + + + + diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json index bcc2108fe5..6d8a5c58c5 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json @@ -84,10 +84,10 @@ "DisplayName:Abp.Identity.Lockout.LockoutDuration": "Lockout duration(seconds)", "DisplayName:Abp.Identity.Lockout.MaxFailedAccessAttempts": "Max failed access attempts", "DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail": "Require confirmed email", - "DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "Enable phone number confirmation", - "DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "Require confirmed phoneNumber", - "DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled": "Is username update enabled", - "DisplayName:Abp.Identity.User.IsEmailUpdateEnabled": "Is email update enabled", + "DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "Allow users to confirm their phone number", + "DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "Require confirmed phone number", + "DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled": "Allow users to change their usernames", + "DisplayName:Abp.Identity.User.IsEmailUpdateEnabled": "Allow users to change their email addresses", "Description:Abp.Identity.Password.RequiredLength": "The minimum length a password must be.", "Description:Abp.Identity.Password.RequiredUniqueChars": "The minimum number of unique characters which a password must contain.", "Description:Abp.Identity.Password.RequireNonAlphanumeric": "If passwords must contain a non-alphanumeric character.", diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj b/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj index 69546e1493..89bb07688a 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj +++ b/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj @@ -19,11 +19,15 @@ + + + + diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs index 5aeddd8b86..0263e34740 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs @@ -63,6 +63,7 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity [UnitOfWork] public virtual async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { + var clientId = context.Request?.Client?.ClientId; using var scope = ServiceScopeFactory.CreateScope(); await ReplaceEmailToUsernameOfInputIfNeeds(context); @@ -90,7 +91,9 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity new IdentitySecurityLogContext { Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, - Action = IdentityServerSecurityLogActionConsts.LoginSucceeded + Action = IdentityServerSecurityLogActionConsts.LoginSucceeded, + UserName = context.UserName, + ClientId = clientId } ); } @@ -153,7 +156,8 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity { Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, Action = result.ToIdentitySecurityLogAction(), - UserName = context.UserName + UserName = context.UserName, + ClientId = clientId }); } else @@ -165,7 +169,9 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() { Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, - Action = IdentityServerSecurityLogActionConsts.LoginInvalidUserName + Action = IdentityServerSecurityLogActionConsts.LoginInvalidUserName, + UserName = context.UserName, + ClientId = clientId }); } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Volo.Abp.PermissionManagement.Web.csproj b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Volo.Abp.PermissionManagement.Web.csproj index 5181fec0b4..e18160a798 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Volo.Abp.PermissionManagement.Web.csproj +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Volo.Abp.PermissionManagement.Web.csproj @@ -16,8 +16,12 @@ + + + + diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj index 8777b66256..fa57f448ac 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj @@ -27,8 +27,12 @@ + + + + diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj index caf72888dd..a88df73911 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj @@ -16,8 +16,12 @@ + + + + diff --git a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj index b2f5d2a4a8..9f3d7224d7 100644 --- a/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj +++ b/modules/virtual-file-explorer/src/Volo.Abp.VirtualFileExplorer.Web/Volo.Abp.VirtualFileExplorer.Web.csproj @@ -21,12 +21,16 @@ + + + + diff --git a/npm/get-version.js b/npm/get-version.js deleted file mode 100644 index 5198ee913a..0000000000 --- a/npm/get-version.js +++ /dev/null @@ -1,10 +0,0 @@ -const fse = require('fs-extra'); - -const commonProps = fse.readFileSync('../common.props').toString(); - -const versionTag = ''; -const versionEndTag = ''; -const first = commonProps.indexOf(versionTag) + versionTag.length; -const last = commonProps.indexOf(versionEndTag); - -console.log(commonProps.substring(first, last)); diff --git a/npm/ng-packs/.vscode/settings.json b/npm/ng-packs/.vscode/settings.json index cee01836fc..5125b157d6 100644 --- a/npm/ng-packs/.vscode/settings.json +++ b/npm/ng-packs/.vscode/settings.json @@ -36,9 +36,7 @@ "titleBar.inactiveForeground": "#e7e7e799", "statusBar.background": "#1d70a2", "statusBarItem.hoverBackground": "#258ecd", - "statusBar.foreground": "#e7e7e7", - "statusBar.border": "#1d70a2", - "titleBar.border": "#1d70a2" + "statusBar.foreground": "#e7e7e7" }, "peacock.color": "#1D70A2" } diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index 4aff547480..9ec24387d0 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -35,24 +35,24 @@ "@abp/ng.theme.basic": "~3.0.5", "@abp/ng.theme.shared": "~3.0.5", "@abp/utils": "^3.0.5", - "@angular-builders/jest": "^9.0.1", - "@angular-devkit/build-angular": "~0.1000.0", - "@angular-devkit/build-ng-packagr": "~0.1000.0", - "@angular/animations": "~10.0.0", - "@angular/cli": "~10.0.0", - "@angular/common": "~10.0.0", + "@angular-builders/jest": "^10.0.0", + "@angular-devkit/build-angular": "~0.1000.6", + "@angular-devkit/build-ng-packagr": "~0.1000.6", + "@angular/animations": "~10.0.10", + "@angular/cli": "~10.0.6", + "@angular/common": "~10.0.10", "@angular/compiler": "10.0.0", "@angular/compiler-cli": "10.0.0", - "@angular/core": "~10.0.0", - "@angular/forms": "~10.0.0", - "@angular/language-service": "~10.0.0", - "@angular/localize": "~10.0.0", - "@angular/platform-browser": "~10.0.0", - "@angular/platform-browser-dynamic": "~10.0.0", - "@angular/router": "~10.0.0", - "@fortawesome/fontawesome-free": "^5.13.0", - "@ng-bootstrap/ng-bootstrap": "^6.1.0", - "@ngneat/spectator": "^5.11.0", + "@angular/core": "~10.0.10", + "@angular/forms": "~10.0.10", + "@angular/language-service": "~10.0.10", + "@angular/localize": "~10.0.10", + "@angular/platform-browser": "~10.0.10", + "@angular/platform-browser-dynamic": "~10.0.10", + "@angular/router": "~10.0.10", + "@fortawesome/fontawesome-free": "^5.14.0", + "@ng-bootstrap/ng-bootstrap": "^7.0.0", + "@ngneat/spectator": "^5.13.0", "@ngx-validate/core": "^0.0.11", "@ngxs/devtools-plugin": "^3.6.2", "@ngxs/logger-plugin": "^3.6.2", @@ -60,7 +60,7 @@ "@ngxs/storage-plugin": "^3.6.2", "@ngxs/store": "^3.6.2", "@schematics/angular": "~10.0.5", - "@swimlane/ngx-datatable": "^17.0.0", + "@swimlane/ngx-datatable": "^17.1.0", "@types/jest": "^25.2.3", "@types/node": "^12.11.1", "angular-oauth2-oidc": "^10.0.3", diff --git a/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.html b/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.html index fdc8f23ce2..37ac7f0175 100644 --- a/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.html +++ b/npm/ng-packs/packages/account/src/lib/components/change-password/change-password.component.html @@ -1,5 +1,5 @@
-
+
{ if (PASSWORD_FIELDS.indexOf(String(control.name)) < 0) return errors; @@ -36,6 +38,8 @@ export class ChangePasswordComponent ) {} ngOnInit(): void { + this.hideCurrentPassword = !this.store.selectSnapshot(ProfileState.getProfile).hasPassword; + const passwordValidations = getPasswordValidators(this.store); this.form = this.fb.group( @@ -58,6 +62,8 @@ export class ChangePasswordComponent validators: [comparePasswords(PASSWORD_FIELDS)], }, ); + + if (this.hideCurrentPassword) this.form.removeControl('password'); } onSubmit() { @@ -66,7 +72,7 @@ export class ChangePasswordComponent this.store .dispatch( new ChangePassword({ - currentPassword: this.form.get('password').value, + ...(!this.hideCurrentPassword && { currentPassword: this.form.get('password').value }), newPassword: this.form.get('newPassword').value, }), ) @@ -74,17 +80,18 @@ export class ChangePasswordComponent .subscribe({ next: () => { this.form.reset(); - this.toasterService.success('AbpAccount::PasswordChangedMessage', 'Success', { + this.toasterService.success('AbpAccount::PasswordChangedMessage', '', { life: 5000, }); + + if (this.hideCurrentPassword) { + this.hideCurrentPassword = false; + this.form.addControl('password', new FormControl('', [required])); + } }, error: err => { this.toasterService.error( snq(() => err.error.error.message, 'AbpAccount::DefaultErrorMessage'), - 'Error', - { - life: 7000, - }, ); }, }); diff --git a/npm/ng-packs/packages/account/src/lib/components/manage-profile/manage-profile.component.html b/npm/ng-packs/packages/account/src/lib/components/manage-profile/manage-profile.component.html index 16e0d66f42..ad2d05b76e 100644 --- a/npm/ng-packs/packages/account/src/lib/components/manage-profile/manage-profile.component.html +++ b/npm/ng-packs/packages/account/src/lib/components/manage-profile/manage-profile.component.html @@ -1,11 +1,15 @@
-
+
-
+

diff --git a/npm/ng-packs/packages/account/src/lib/components/manage-profile/manage-profile.component.ts b/npm/ng-packs/packages/account/src/lib/components/manage-profile/manage-profile.component.ts index f9ee2b8ff7..3a795c83d7 100644 --- a/npm/ng-packs/packages/account/src/lib/components/manage-profile/manage-profile.component.ts +++ b/npm/ng-packs/packages/account/src/lib/components/manage-profile/manage-profile.component.ts @@ -1,17 +1,42 @@ import { fadeIn } from '@abp/ng.theme.shared'; import { transition, trigger, useAnimation } from '@angular/animations'; -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { eAccountComponents } from '../../enums/components'; +import { Store } from '@ngxs/store'; +import { GetProfile, ProfileState } from '@abp/ng.core'; @Component({ selector: 'abp-manage-profile', templateUrl: './manage-profile.component.html', animations: [trigger('fadeIn', [transition(':enter', useAnimation(fadeIn))])], + styles: [ + ` + .min-h-400 { + min-height: 400px; + } + `, + ], }) -export class ManageProfileComponent { +export class ManageProfileComponent implements OnInit { selectedTab = 0; changePasswordKey = eAccountComponents.ChangePassword; personalSettingsKey = eAccountComponents.PersonalSettings; + + isProfileLoaded: boolean; + + hideChangePasswordTab: boolean; + + constructor(private store: Store) {} + + ngOnInit() { + this.store.dispatch(new GetProfile()).subscribe(() => { + this.isProfileLoaded = true; + if (this.store.selectSnapshot(ProfileState.getProfile).isExternal) { + this.hideChangePasswordTab = true; + this.selectedTab = 1; + } + }); + } } diff --git a/npm/ng-packs/packages/account/src/lib/components/personal-settings/personal-settings.component.ts b/npm/ng-packs/packages/account/src/lib/components/personal-settings/personal-settings.component.ts index 444a014d6d..f31cb2743e 100644 --- a/npm/ng-packs/packages/account/src/lib/components/personal-settings/personal-settings.component.ts +++ b/npm/ng-packs/packages/account/src/lib/components/personal-settings/personal-settings.component.ts @@ -1,10 +1,9 @@ -import { GetProfile, Profile, ProfileState, UpdateProfile } from '@abp/ng.core'; +import { ProfileState, UpdateProfile } from '@abp/ng.core'; +import { ToasterService } from '@abp/ng.theme.shared'; import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { Select, Store } from '@ngxs/store'; -import { Observable } from 'rxjs'; -import { take, withLatestFrom, finalize } from 'rxjs/operators'; -import { ToasterService } from '@abp/ng.theme.shared'; +import { Store } from '@ngxs/store'; +import { finalize } from 'rxjs/operators'; import { Account } from '../../models/account'; const { maxLength, required, email } = Validators; @@ -19,9 +18,6 @@ export class PersonalSettingsComponent OnInit, Account.PersonalSettingsComponentInputs, Account.PersonalSettingsComponentOutputs { - @Select(ProfileState.getProfile) - profile$: Observable; - form: FormGroup; inProgress: boolean; @@ -37,18 +33,15 @@ export class PersonalSettingsComponent } buildForm() { - this.store - .dispatch(new GetProfile()) - .pipe(withLatestFrom(this.profile$), take(1)) - .subscribe(([, profile]) => { - this.form = this.fb.group({ - userName: [profile.userName, [required, maxLength(256)]], - email: [profile.email, [required, email, maxLength(256)]], - name: [profile.name || '', [maxLength(64)]], - surname: [profile.surname || '', [maxLength(64)]], - phoneNumber: [profile.phoneNumber || '', [maxLength(16)]], - }); - }); + const profile = this.store.selectSnapshot(ProfileState.getProfile); + + this.form = this.fb.group({ + userName: [profile.userName, [required, maxLength(256)]], + email: [profile.email, [required, email, maxLength(256)]], + name: [profile.name || '', [maxLength(64)]], + surname: [profile.surname || '', [maxLength(64)]], + phoneNumber: [profile.phoneNumber || '', [maxLength(16)]], + }); } submit() { diff --git a/npm/ng-packs/packages/core/package.json b/npm/ng-packs/packages/core/package.json index 4ccafae3a0..254848b3e8 100644 --- a/npm/ng-packs/packages/core/package.json +++ b/npm/ng-packs/packages/core/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@abp/utils": "^3.0.5", - "@angular/localize": "~9.1.11", + "@angular/localize": "~10.0.10", "@ngxs/router-plugin": "^3.6.2", "@ngxs/storage-plugin": "^3.6.2", "@ngxs/store": "^3.6.2", @@ -16,7 +16,7 @@ "just-clone": "^3.1.0", "just-compare": "^1.3.0", "snq": "^1.0.3", - "ts-toolbelt": "^6.9.9", + "ts-toolbelt": "^6.15.5", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/core/src/lib/core.module.ts b/npm/ng-packs/packages/core/src/lib/core.module.ts index 85cf87297f..c74591d204 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -4,7 +4,11 @@ import { APP_INITIALIZER, Injector, ModuleWithProviders, NgModule } from '@angul import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { NgxsRouterPluginModule } from '@ngxs/router-plugin'; -import { NgxsStoragePluginModule } from '@ngxs/storage-plugin'; +import { + NgxsStoragePluginModule, + NGXS_STORAGE_PLUGIN_OPTIONS, + StorageOption, +} from '@ngxs/storage-plugin'; import { NgxsModule, NGXS_PLUGINS } from '@ngxs/store'; import { OAuthModule, OAuthStorage } from 'angular-oauth2-oidc'; import { AbstractNgModelComponent } from './abstracts/ng-model.component'; @@ -118,7 +122,7 @@ export class BaseCoreModule {} LocalizationModule, NgxsModule.forFeature([ReplaceableComponentsState, ProfileState, SessionState, ConfigState]), NgxsRouterPluginModule.forRoot(), - NgxsStoragePluginModule.forRoot({ key: ['SessionState'] }), + NgxsStoragePluginModule.forRoot(), OAuthModule.forRoot(), ], }) @@ -216,7 +220,23 @@ export class CoreModule { useFactory: noop, }, { provide: OAuthStorage, useFactory: storageFactory }, + { + provide: NGXS_STORAGE_PLUGIN_OPTIONS, + useValue: { + storage: StorageOption.LocalStorage, + serialize: JSON.stringify, + deserialize: JSON.parse, + beforeSerialize: ngxsStoragePluginSerialize, + afterDeserialize: ngxsStoragePluginSerialize, + ...options.ngxsStoragePluginOptions, + key: [...(options.ngxsStoragePluginOptions?.key || []), 'SessionState'], + }, + }, ], }; } } + +export function ngxsStoragePluginSerialize(data) { + return data; +} diff --git a/npm/ng-packs/packages/core/src/lib/models/common.ts b/npm/ng-packs/packages/core/src/lib/models/common.ts index 85b1138002..b01aed1f26 100644 --- a/npm/ng-packs/packages/core/src/lib/models/common.ts +++ b/npm/ng-packs/packages/core/src/lib/models/common.ts @@ -3,6 +3,7 @@ import { Router } from '@angular/router'; import { Subject } from 'rxjs'; import { eLayoutType } from '../enums/common'; import { Config } from './config'; +import { NgxsStoragePluginOptions } from '@ngxs/storage-plugin'; export namespace ABP { export interface Root { @@ -10,6 +11,7 @@ export namespace ABP { skipGetAppConfiguration?: boolean; sendNullsAsQueryParam?: boolean; cultureNameLocaleFileMap?: Dictionary; + ngxsStoragePluginOptions?: NgxsStoragePluginOptions & { key?: string[] }; } export interface Test { diff --git a/npm/ng-packs/packages/core/src/lib/models/config.ts b/npm/ng-packs/packages/core/src/lib/models/config.ts index e3d6f6ab70..5f19907388 100644 --- a/npm/ng-packs/packages/core/src/lib/models/config.ts +++ b/npm/ng-packs/packages/core/src/lib/models/config.ts @@ -42,9 +42,14 @@ export namespace Config { } export type LocalizationParam = string | LocalizationWithDefault; + export type customMergeFn = ( + localEnv: Partial, + remoteEnv: any, + ) => Config.Environment; export interface RemoteEnv { url: string; + mergeStrategy: 'deepmerge' | 'overwrite' | customMergeFn; method?: string; headers?: ABP.Dictionary; } diff --git a/npm/ng-packs/packages/core/src/lib/models/profile.ts b/npm/ng-packs/packages/core/src/lib/models/profile.ts index 068a3fad39..779c8c3b6a 100644 --- a/npm/ng-packs/packages/core/src/lib/models/profile.ts +++ b/npm/ng-packs/packages/core/src/lib/models/profile.ts @@ -1,14 +1,24 @@ +import { ExtensibleObject } from './dtos'; + export namespace Profile { export interface State { profile: Response; } - export interface Response { + export interface Response extends Partial { userName: string; email: string; name: string; surname: string; phoneNumber: string; + /** + * Following 4 fields are added as optional (for backward compatibility) on 17.08.2020 + * Also, this interface extends `ExtensibleObject` as partial for extraProperties field. + */ + isExternal?: boolean; + hasPassword?: boolean; + emailConfirmed?: boolean; + phoneNumberConfirmed?: boolean; } export interface ChangePasswordRequest { diff --git a/npm/ng-packs/packages/core/src/lib/services/auth.service.ts b/npm/ng-packs/packages/core/src/lib/services/auth.service.ts index 6b86b580df..94a48c00a4 100644 --- a/npm/ng-packs/packages/core/src/lib/services/auth.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/auth.service.ts @@ -57,16 +57,13 @@ export class AuthService { login(username: string, password: string): Observable { const tenant = this.store.selectSnapshot(SessionState.getTenant); - return from(this.oAuthService.loadDiscoveryDocument()).pipe( - switchMap(() => - from( - this.oAuthService.fetchTokenUsingPasswordFlow( - username, - password, - new HttpHeaders({ ...(tenant && tenant.id && { __tenant: tenant.id }) }), - ), - ), + return from( + this.oAuthService.fetchTokenUsingPasswordFlow( + username, + password, + new HttpHeaders({ ...(tenant && tenant.id && { __tenant: tenant.id }) }), ), + ).pipe( switchMap(() => this.store.dispatch(new GetAppConfiguration())), tap(() => { const redirectUrl = diff --git a/npm/ng-packs/packages/core/src/lib/states/session.state.ts b/npm/ng-packs/packages/core/src/lib/states/session.state.ts index 0c81a46c58..a9bb89d3f0 100644 --- a/npm/ng-packs/packages/core/src/lib/states/session.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/session.state.ts @@ -46,7 +46,7 @@ export class SessionState { .pipe(ofActionSuccessful(GetAppConfiguration)) .pipe(take(1)) .subscribe(() => { - const { sessionDetail } = this.store.selectSnapshot(SessionState) || { sessionDetail: {} }; + const sessionDetail = this.store.selectSnapshot(SessionState)?.sessionDetail || {}; const fiveMinutesBefore = new Date().valueOf() - 5 * 60 * 1000; diff --git a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts b/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts index b7aa5601e2..19fa4f509c 100644 --- a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts +++ b/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts @@ -1,17 +1,18 @@ import { Injector } from '@angular/core'; +import { Router } from '@angular/router'; import { Store } from '@ngxs/store'; import { AuthConfig, OAuthService } from 'angular-oauth2-oidc'; -import { ConfigState } from '../states/config.state'; -import { CORE_OPTIONS } from '../tokens/options.token'; -import { Router } from '@angular/router'; import { Observable, of } from 'rxjs'; -import { RestService } from '../services/rest.service'; -import { switchMap } from 'rxjs/operators'; +import { switchMap, tap } from 'rxjs/operators'; import { GetAppConfiguration } from '../actions/config.actions'; +import { RestOccurError } from '../actions/rest.actions'; +import { RestService } from '../services/rest.service'; +import { ConfigState } from '../states/config.state'; export abstract class AuthFlowStrategy { abstract readonly isInternalAuth: boolean; + protected store: Store; protected oAuthService: OAuthService; protected oAuthConfig: AuthConfig; abstract checkIfInternalAuth(): boolean; @@ -19,13 +20,12 @@ export abstract class AuthFlowStrategy { abstract logout(): Observable; abstract destroy(): void; - private catchError = err => { - // TODO: handle the error - }; + private catchError = err => this.store.dispatch(new RestOccurError(err)); constructor(protected injector: Injector) { + this.store = injector.get(Store); this.oAuthService = injector.get(OAuthService); - this.oAuthConfig = injector.get(CORE_OPTIONS).environment.oAuthConfig; + this.oAuthConfig = injector.get(Store).selectSnapshot(ConfigState.getDeep('environment.oAuthConfig')); } async init(): Promise { @@ -74,10 +74,9 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy { } logout() { - const store = this.injector.get(Store); const rest = this.injector.get(RestService); - const issuer = store.selectSnapshot(ConfigState.getDeep('environment.oAuthConfig.issuer')); + const issuer = this.store.selectSnapshot(ConfigState.getDeep('environment.oAuthConfig.issuer')); return rest .request( { @@ -88,10 +87,8 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy { issuer, ) .pipe( - switchMap(() => { - this.oAuthService.logOut(); - return store.dispatch(new GetAppConfiguration()); - }), + tap(() => this.oAuthService.logOut()), + switchMap(() => this.store.dispatch(new GetAppConfiguration())), ); } diff --git a/npm/ng-packs/packages/core/src/lib/tests/common-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/common-utils.spec.ts index 60280e396b..05be159b9e 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/common-utils.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/common-utils.spec.ts @@ -1,4 +1,12 @@ -import { isUndefinedOrEmptyString, noop } from '../utils'; +import { + isUndefinedOrEmptyString, + noop, + isNullOrUndefined, + exists, + isObject, + isArray, + isObjectAndNotArray, +} from '../utils'; describe('CommonUtils', () => { describe('#noop', () => { @@ -23,4 +31,88 @@ describe('CommonUtils', () => { expect(isUndefinedOrEmptyString(value)).toBe(expected); }); }); + + describe('#isNullOrUndefined & #exists', () => { + test.each` + value | expected + ${null} | ${true} + ${undefined} | ${true} + ${true} | ${false} + ${false} | ${false} + ${''} | ${false} + ${'test'} | ${false} + ${0} | ${false} + ${10} | ${false} + ${[]} | ${false} + ${[1, 2]} | ${false} + ${{}} | ${false} + ${{ a: 1 }} | ${false} + `( + 'should return $expected and !$expected (for #exists) when given parameter is $value', + ({ value, expected }) => { + expect(isNullOrUndefined(value)).toBe(expected); + expect(exists(value)).toBe(!expected); + }, + ); + }); + + describe('#isObject', () => { + test.each` + value | expected + ${null} | ${false} + ${undefined} | ${false} + ${true} | ${false} + ${false} | ${false} + ${''} | ${false} + ${'test'} | ${false} + ${0} | ${false} + ${10} | ${false} + ${[]} | ${true} + ${[1, 2]} | ${true} + ${{}} | ${true} + ${{ a: 1 }} | ${true} + `('should return $expected when given parameter is $value', ({ value, expected }) => { + expect(isObject(value)).toBe(expected); + }); + }); + + describe('#isArray', () => { + test.each` + value | expected + ${null} | ${false} + ${undefined} | ${false} + ${true} | ${false} + ${false} | ${false} + ${''} | ${false} + ${'test'} | ${false} + ${0} | ${false} + ${10} | ${false} + ${[]} | ${true} + ${[1, 2]} | ${true} + ${{}} | ${false} + ${{ a: 1 }} | ${false} + `('should return $expected when given parameter is $value', ({ value, expected }) => { + expect(isArray(value)).toBe(expected); + }); + }); + + describe('#isObjectAndNotArray', () => { + test.each` + value | expected + ${null} | ${false} + ${undefined} | ${false} + ${true} | ${false} + ${false} | ${false} + ${''} | ${false} + ${'test'} | ${false} + ${0} | ${false} + ${10} | ${false} + ${[]} | ${false} + ${[1, 2]} | ${false} + ${{}} | ${true} + ${{ a: 1 }} | ${true} + `('should return $expected when given parameter is $value', ({ value, expected }) => { + expect(isObjectAndNotArray(value)).toBe(expected); + }); + }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/date-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/date-utils.spec.ts new file mode 100644 index 0000000000..ba8413e5ef --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/date-utils.spec.ts @@ -0,0 +1,54 @@ +import { + ConfigStateService, + getShortDateFormat, + getShortDateShortTimeFormat, + getShortTimeFormat, +} from '@abp/ng.core'; + +const dateTimeFormat = { + calendarAlgorithmType: 'SolarCalendar', + dateSeparator: '/', + dateTimeFormatLong: 'dddd, MMMM d, yyyy', + fullDateTimePattern: 'dddd, MMMM d, yyyy h:mm:ss tt', + longTimePattern: 'h:mm:ss tt', + shortDatePattern: 'M/d/yyyy', + shortTimePattern: 'h:mm tt', +}; + +describe('Date Utils', () => { + let config: ConfigStateService; + + beforeEach(() => { + config = new ConfigStateService(undefined); + }); + + describe('#getShortDateFormat', () => { + test('should get the short date format from ConfigState and return it', () => { + const getDeepSpy = jest.spyOn(config, 'getDeep'); + getDeepSpy.mockReturnValueOnce(dateTimeFormat); + + expect(getShortDateFormat(config)).toBe('M/d/yyyy'); + expect(getDeepSpy).toHaveBeenCalledWith('localization.currentCulture.dateTimeFormat'); + }); + }); + + describe('#getShortTimeFormat', () => { + test('should get the short time format from ConfigState and return it', () => { + const getDeepSpy = jest.spyOn(config, 'getDeep'); + getDeepSpy.mockReturnValueOnce(dateTimeFormat); + + expect(getShortTimeFormat(config)).toBe('h:mm a'); + expect(getDeepSpy).toHaveBeenCalledWith('localization.currentCulture.dateTimeFormat'); + }); + }); + + describe('#getShortDateShortTimeFormat', () => { + test('should get the short date time format from ConfigState and return it', () => { + const getDeepSpy = jest.spyOn(config, 'getDeep'); + getDeepSpy.mockReturnValueOnce(dateTimeFormat); + + expect(getShortDateShortTimeFormat(config)).toBe('M/d/yyyy h:mm a'); + expect(getDeepSpy).toHaveBeenCalledWith('localization.currentCulture.dateTimeFormat'); + }); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts index ab7cc0df0c..6dcfba17a7 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts @@ -5,29 +5,8 @@ import { Store } from '@ngxs/store'; import { BehaviorSubject, of } from 'rxjs'; import { getRemoteEnv } from '../utils/environment-utils'; import { SetEnvironment } from '../actions/config.actions'; - -const environment = { - production: false, - hmr: false, - application: { - baseUrl: 'https://volosoft.com', - name: 'MyProjectName', - logoUrl: '', - }, - oAuthConfig: { - issuer: 'https://api.volosoft.com', - clientId: 'MyProjectName_App', - dummyClientSecret: '1q2w3e*', - scope: 'MyProjectName', - oidc: false, - requireHttps: true, - }, - apis: { - default: { - url: 'https://api.volosoft.com', - }, - }, -}; +import { Config } from '../models/config'; +import { deepMerge } from '../utils/object-utils'; @Component({ selector: 'abp-dummy', @@ -44,8 +23,60 @@ describe('EnvironmentUtils', () => { beforeEach(() => (spectator = createComponent())); - describe('#getRemoteEnv', async () => { - test('should call the remoteEnv URL and dispatch the SetEnvironment action ', async () => { + describe('#getRemoteEnv', () => { + const environment: Config.Environment = { + production: false, + hmr: false, + application: { + baseUrl: 'https://volosoft.com', + name: 'MyProjectName', + logoUrl: '', + }, + remoteEnv: { url: '/assets/appsettings.json', mergeStrategy: 'deepmerge' }, + oAuthConfig: { + issuer: 'https://api.volosoft.com', + clientId: 'MyProjectName_App', + dummyClientSecret: '1q2w3e*', + scope: 'MyProjectName', + oidc: false, + requireHttps: true, + }, + apis: { + default: { + url: 'https://api.volosoft.com', + }, + }, + }; + + const customEnv = { + application: { + baseUrl: 'https://custom-volosoft.com', + name: 'Custom-MyProjectName', + logoUrl: 'https://logourl/', + }, + apis: { + default: { + url: 'https://test-api.volosoft.com', + }, + }, + }; + + const someEnv = { apiUrl: 'https://some-api-url' } as any; + const customFn = (_, __) => someEnv; + + test.each` + case | strategy | expected + ${'null'} | ${null} | ${customEnv} + ${'undefined'} | ${undefined} | ${customEnv} + ${'overwrite'} | ${'overwrite'} | ${customEnv} + ${'deepmerge'} | ${'deepmerge'} | ${deepMerge(environment, customEnv)} + ${'customFn'} | ${customFn} | ${someEnv} + `( + 'should call the remoteEnv URL and dispatch the SetEnvironment action for case $case ', + ({ strategy, expected }) => setupTestAndRun({ mergeStrategy: strategy }, expected), + ); + + function setupTestAndRun(strategy: Pick, expectedValue) { const injector = spectator.inject(Injector); const injectorSpy = jest.spyOn(injector, 'get'); const store = spectator.inject(Store); @@ -56,16 +87,14 @@ describe('EnvironmentUtils', () => { injectorSpy.mockReturnValueOnce(http); injectorSpy.mockReturnValueOnce(store); - requestSpy.mockReturnValue(new BehaviorSubject(environment)); + requestSpy.mockReturnValue(new BehaviorSubject(customEnv)); dispatchSpy.mockReturnValue(of(true)); - const partialEnv = { remoteEnv: { url: '/assets/appsettings.json' } }; - getRemoteEnv(injector, partialEnv); + environment.remoteEnv.mergeStrategy = strategy.mergeStrategy; + getRemoteEnv(injector, environment); expect(requestSpy).toHaveBeenCalledWith('GET', '/assets/appsettings.json', { headers: {} }); - expect(dispatchSpy).toHaveBeenCalledWith( - new SetEnvironment({ ...environment, ...partialEnv }), - ); - }); + expect(dispatchSpy).toHaveBeenCalledWith(new SetEnvironment(expectedValue)); + } }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/object-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/object-utils.spec.ts new file mode 100644 index 0000000000..235d2e90ed --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/object-utils.spec.ts @@ -0,0 +1,141 @@ +import { deepMerge } from '../utils/object-utils'; + +describe('DeepMerge', () => { + test.each` + target | source + ${null} | ${null} + ${null} | ${undefined} + ${undefined} | ${null} + ${undefined} | ${undefined} + `('should return empty object when both inputs are $target and $source', ({ target, source }) => { + expect(deepMerge(target, source)).toEqual({}); + }); + + test.each` + value + ${10} + ${false} + ${''} + ${'test-string'} + ${{ a: 1 }} + ${[1, 2, 3]} + ${{}} + `('should correctly return when any of the inputs is null or undefined', val => { + expect(deepMerge(undefined, val)).toEqual(val); + expect(deepMerge(null, val)).toEqual(val); + expect(deepMerge(val, undefined)).toEqual(val); + expect(deepMerge(val, null)).toEqual(val); + }); + + test.each` + target | source + ${10} | ${false} + ${false} | ${20} + ${'some-string'} | ${{ a: 5 }} + ${{ b: 10 }} | ${50} + ${[1, 2, 3]} | ${40} + ${{ k: 60 }} | ${[4, 5, 6]} + `( + 'should correctly return source if one of them is primitive or an array', + ({ target, source }) => { + expect(deepMerge(target, source)).toEqual(source); + }, + ); + + it('should correctly return when both inputs are objects with different fields', () => { + const target = { a: 1 }; + const source = { b: 2 }; + const expected = { a: 1, b: 2 }; + expect(deepMerge(target, source)).toEqual(expected); + expect(deepMerge(source, target)).toEqual(expected); + }); + + it('should correctly return when both inputs are object with same fields but different values', () => { + const target = { a: 1 }; + const source = { a: 5 }; + expect(deepMerge(target, source)).toEqual(source); + expect(deepMerge(source, target)).toEqual(target); + }); + + it('should correctly merge shallow objects with different fields as well as some shared ones', () => { + const target = { a: 1, b: 2, c: 3 }; + const source = { a: 4, d: 5, e: 6 }; + expect(deepMerge(target, source)).toEqual({ a: 4, b: 2, c: 3, d: 5, e: 6 }); + }); + + it('should not merge arrays and return the latter', () => { + const firstArray = [1, 2, 3]; + const secondArray = [3, 4, 5, 6]; + expect(deepMerge(firstArray, secondArray)).toEqual(secondArray); + const target = { a: firstArray }; + const source = { a: secondArray }; + expect(deepMerge(target, source)).toEqual({ a: secondArray }); + }); + + it('should correctly merge nested objects', () => { + const target = { + a: { + b: { + c: { + d: 1, + g: 10, + q: undefined, + t: false, + }, + e: { + f: [1, 2, 3], + p: 'other-string', + }, + }, + }, + x: { + q: 'some-string', + }, + }; + const source = { + a: { + b: { + c: { + h: 30, + q: 45, + t: null, + }, + m: 20, + e: { + f: [20, 30, 40], + }, + }, + e: { k: [5, 6] }, + }, + z: { + y: true, + }, + }; + const expected = { + a: { + b: { + c: { + d: 1, + g: 10, + h: 30, + q: 45, + t: false, + }, + e: { + f: [20, 30, 40], + p: 'other-string', + }, + m: 20, + }, + e: { k: [5, 6] }, + }, + x: { + q: 'some-string', + }, + z: { + y: true, + }, + }; + expect(deepMerge(target, source)).toEqual(expected); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/profile.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/profile.service.spec.ts index 4c7b7ba3c1..7ccb0f2da3 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/profile.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/profile.service.spec.ts @@ -37,6 +37,8 @@ describe('ProfileService', () => { name: 'John', surname: 'Doe', phoneNumber: '+123456', + isExternal: false, + hasPassword: false, }; spectator.inject(Store).selectSnapshot.andReturn('https://abp.io'); spectator.service.update(mock).subscribe(); diff --git a/npm/ng-packs/packages/core/src/lib/tokens/options.token.ts b/npm/ng-packs/packages/core/src/lib/tokens/options.token.ts index 4cbb13da76..ac51dbb92e 100644 --- a/npm/ng-packs/packages/core/src/lib/tokens/options.token.ts +++ b/npm/ng-packs/packages/core/src/lib/tokens/options.token.ts @@ -1,15 +1,12 @@ import { InjectionToken } from '@angular/core'; -import { ABP } from '../models/common'; import differentLocales from '../constants/different-locales'; +import { ABP } from '../models/common'; export const CORE_OPTIONS = new InjectionToken('CORE_OPTIONS'); -export function coreOptionsFactory({ - cultureNameLocaleFileMap: localeNameMap = {}, - ...options -}: ABP.Root) { +export function coreOptionsFactory({ cultureNameLocaleFileMap = {}, ...options }: ABP.Root) { return { ...options, - cultureNameLocaleFileMap: { ...differentLocales, ...localeNameMap }, + cultureNameLocaleFileMap: { ...differentLocales, ...cultureNameLocaleFileMap }, } as ABP.Root; } diff --git a/npm/ng-packs/packages/core/src/lib/utils/common-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/common-utils.ts index b8609be130..fb634d6dda 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/common-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/common-utils.ts @@ -7,3 +7,23 @@ export function noop() { export function isUndefinedOrEmptyString(value: unknown): boolean { return value === undefined || value === ''; } + +export function isNullOrUndefined(obj) { + return obj === null || obj === undefined; +} + +export function exists(obj) { + return !isNullOrUndefined(obj); +} + +export function isObject(obj) { + return obj instanceof Object; +} + +export function isArray(obj) { + return Array.isArray(obj); +} + +export function isObjectAndNotArray(obj) { + return isObject(obj) && !isArray(obj); +} diff --git a/npm/ng-packs/packages/core/src/lib/utils/date-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/date-utils.ts new file mode 100644 index 0000000000..ee44fb99cc --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/utils/date-utils.ts @@ -0,0 +1,26 @@ +import { ApplicationConfiguration } from '../models/application-configuration'; +import { ConfigStateService } from '../services'; + +export function getShortDateFormat(configStateService: ConfigStateService) { + const dateTimeFormat = configStateService.getDeep( + 'localization.currentCulture.dateTimeFormat', + ) as ApplicationConfiguration.DateTimeFormat; + + return dateTimeFormat.shortDatePattern; +} + +export function getShortTimeFormat(configStateService: ConfigStateService) { + const dateTimeFormat = configStateService.getDeep( + 'localization.currentCulture.dateTimeFormat', + ) as ApplicationConfiguration.DateTimeFormat; + + return dateTimeFormat.shortTimePattern.replace('tt', 'a'); +} + +export function getShortDateShortTimeFormat(configStateService: ConfigStateService) { + const dateTimeFormat = configStateService.getDeep( + 'localization.currentCulture.dateTimeFormat', + ) as ApplicationConfiguration.DateTimeFormat; + + return `${dateTimeFormat.shortDatePattern} ${dateTimeFormat.shortTimePattern.replace('tt', 'a')}`; +} diff --git a/npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts index c8d92603f3..47a8f18b42 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts @@ -5,6 +5,7 @@ import { catchError, switchMap } from 'rxjs/operators'; import { SetEnvironment } from '../actions/config.actions'; import { Config } from '../models/config'; import { RestOccurError } from '../actions/rest.actions'; +import { deepMerge } from './object-utils'; export function getRemoteEnv(injector: Injector, environment: Partial) { const { remoteEnv } = environment; @@ -18,7 +19,24 @@ export function getRemoteEnv(injector: Injector, environment: Partial(method, url, { headers }) .pipe( catchError(err => store.dispatch(new RestOccurError(err))), // TODO: Condiser get handle function from a provider - switchMap(env => store.dispatch(new SetEnvironment({ ...environment, ...env }))), + switchMap(env => store.dispatch(mergeEnvironments(environment, env, remoteEnv))), ) .toPromise(); } + +function mergeEnvironments( + local: Partial, + remote: any, + config: Config.RemoteEnv, +) { + switch (config.mergeStrategy) { + case 'deepmerge': + return new SetEnvironment(deepMerge(local, remote)); + case 'overwrite': + case null: + case undefined: + return new SetEnvironment(remote); + default: + return new SetEnvironment(config.mergeStrategy(local, remote)); + } +} diff --git a/npm/ng-packs/packages/core/src/lib/utils/index.ts b/npm/ng-packs/packages/core/src/lib/utils/index.ts index 48efb760e9..383261fc07 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/index.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/index.ts @@ -1,5 +1,6 @@ export * from './array-utils'; export * from './common-utils'; +export * from './date-utils'; export * from './environment-utils'; export * from './factory-utils'; export * from './form-utils'; @@ -9,6 +10,7 @@ export * from './lazy-load-utils'; export * from './localization-utils'; export * from './multi-tenancy-utils'; export * from './number-utils'; +export * from './object-utils'; export * from './route-utils'; export * from './rxjs-utils'; export * from './string-utils'; diff --git a/npm/ng-packs/packages/core/src/lib/utils/object-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/object-utils.ts new file mode 100644 index 0000000000..b38c8a62ef --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/utils/object-utils.ts @@ -0,0 +1,37 @@ +import { isObjectAndNotArray, isNullOrUndefined, exists, isArray, isObject } from './common-utils'; + +export function deepMerge(target, source) { + if (isObjectAndNotArray(target) && isObjectAndNotArray(source)) { + return deepMergeRecursively(target, source); + } else if (isNullOrUndefined(target) && isNullOrUndefined(source)) { + return {}; + } else { + return exists(source) ? source : target; + } +} + +function deepMergeRecursively(target, source) { + const shouldNotRecurse = + isNullOrUndefined(target) || + isNullOrUndefined(source) || // at least one not defined + isArray(target) || + isArray(source) || // at least one array + !isObject(target) || + !isObject(source); // at least one not an object + + /** + * if we will not recurse any further, + * we will prioritize source if it is a defined value. + */ + if (shouldNotRecurse) { + return exists(source) ? source : target; + } + + const keysOfTarget = Object.keys(target); + const keysOfSource = Object.keys(source); + const uniqueKeys = new Set(keysOfTarget.concat(keysOfSource)); + return [...uniqueKeys].reduce((retVal, key) => { + retVal[key] = deepMergeRecursively(target[key], source[key]); + return retVal; + }, {}); +} diff --git a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html index 087a69a75c..6ee1766db7 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html +++ b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html @@ -10,11 +10,11 @@ *ngFor="let feature of features$ | async; let i = index" [ngSwitch]="feature.valueType.name" > -
{{ feature.name }}
-
+
{{ feature.displayName }}
+
-
+
diff --git a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts index b8aba035bd..cd4d00491b 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts @@ -87,8 +87,8 @@ export class FeatureManagementComponent let features = this.store.selectSnapshot(FeatureManagementState.getFeatures); features = features.map((feature, i) => ({ - name: feature.name, - value: !this.form.value[i] || this.form.value[i] === 'false' ? null : this.form.value[i], + ...feature, + value: this.form.value[i], })); this.store diff --git a/npm/ng-packs/packages/feature-management/src/lib/models/feature-management.ts b/npm/ng-packs/packages/feature-management/src/lib/models/feature-management.ts index fa0165ae2e..02f6f687e2 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/models/feature-management.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/models/feature-management.ts @@ -13,6 +13,7 @@ export namespace FeatureManagement { export interface Feature { name: string; + displayName: string; value: string; description?: string; valueType?: ValueType; diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html b/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html index dc09088430..7cb90c3329 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html +++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html @@ -1,5 +1,6 @@
diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.ts index adacc4efa8..84b47a02ac 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.ts @@ -1,4 +1,10 @@ -import { ListService } from '@abp/ng.core'; +import { + ListService, + ConfigStateService, + getShortDateFormat, + getShortDateShortTimeFormat, + getShortTimeFormat, +} from '@abp/ng.core'; import { formatDate } from '@angular/common'; import { ChangeDetectionStrategy, @@ -22,6 +28,7 @@ import { EntityProp, EntityPropList } from '../../models/entity-props'; import { PropData } from '../../models/props'; import { ExtensionsService } from '../../services/extensions.service'; import { EXTENSIONS_IDENTIFIER } from '../../tokens/extensions.token'; +import { EntityActionList } from '../../models/entity-actions'; const DEFAULT_ACTIONS_COLUMN_WIDTH = 150; @Component({ @@ -31,7 +38,15 @@ const DEFAULT_ACTIONS_COLUMN_WIDTH = 150; changeDetection: ChangeDetectionStrategy.OnPush, }) export class ExtensibleTableComponent implements OnChanges { - @Input() actionsText: string; + protected _actionsText: string; + @Input() + set actionsText(value: string) { + this._actionsText = value; + } + get actionsText(): string { + return this._actionsText ?? (this.actionList.length > 1 ? 'AbpUi::Actions' : ''); + } + @Input() data: R[]; @Input() list: ListService; @Input() recordsTotal: number; @@ -46,14 +61,22 @@ export class ExtensibleTableComponent implements OnChanges { readonly propList: EntityPropList; + readonly actionList: EntityActionList; + readonly trackByFn: TrackByFunction> = (_, item) => item.name; - constructor(@Inject(LOCALE_ID) private locale: string, injector: Injector) { + constructor( + @Inject(LOCALE_ID) private locale: string, + private config: ConfigStateService, + injector: Injector, + ) { // tslint:disable-next-line this.getInjected = injector.get.bind(injector); const extensions = injector.get(ExtensionsService); const name = injector.get(EXTENSIONS_IDENTIFIER); this.propList = extensions.entityProps.get(name).props; + this.actionList = (extensions['entityActions'].get(name) + .actions as unknown) as EntityActionList; this.setColumnWidths(DEFAULT_ACTIONS_COLUMN_WIDTH); } @@ -82,11 +105,11 @@ export class ExtensibleTableComponent implements OnChanges { case ePropType.Boolean: return this.getIcon(value); case ePropType.Date: - return this.getDate(value, 'yyyy-MM-dd'); + return this.getDate(value, getShortDateFormat(this.config)); case ePropType.Time: - return this.getDate(value, 'HH:mm'); + return this.getDate(value, getShortTimeFormat(this.config)); case ePropType.DateTime: - return this.getDate(value, 'yyyy-MM-dd HH:mm:ss Z'); + return this.getDate(value, getShortDateShortTimeFormat(this.config)); default: return value; // More types can be handled in the future diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/grid-actions/grid-actions.component.html b/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/grid-actions/grid-actions.component.html index 04ac1017a4..fdf6c19e8b 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/grid-actions/grid-actions.component.html +++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/grid-actions/grid-actions.component.html @@ -1,4 +1,4 @@ -
+
- - +
+ + + + + + diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/entity-props.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/entity-props.ts index a49a8e8d41..14fd37ad84 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/entity-props.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/entity-props.ts @@ -11,6 +11,7 @@ import { Props, PropsFactory, } from './props'; +import { ActionCallback } from './actions'; export class EntityPropList extends PropList> {} @@ -26,6 +27,7 @@ export class EntityProp extends Prop { readonly columnWidth: number | undefined; readonly sortable: boolean; readonly valueResolver: PropCallback>; + readonly action: ActionCallback; constructor(options: EntityPropOptions) { super( @@ -40,6 +42,7 @@ export class EntityProp extends Prop { this.columnWidth = options.columnWidth; this.sortable = options.sortable || false; this.valueResolver = options.valueResolver || (data => of(data.record[this.name])); + this.action = options.action || (_ => {}); } static create(options: EntityPropOptions) { @@ -60,6 +63,7 @@ export type EntityPropOptions = O.Optional< | 'columnWidth' | 'sortable' | 'valueResolver' + | 'action' >; export type EntityPropDefaults = Record[]>; diff --git a/npm/ng-packs/packages/theme-shared/package.json b/npm/ng-packs/packages/theme-shared/package.json index ee2b5d7ffa..1f593fb29b 100644 --- a/npm/ng-packs/packages/theme-shared/package.json +++ b/npm/ng-packs/packages/theme-shared/package.json @@ -8,10 +8,10 @@ }, "dependencies": { "@abp/ng.core": "~3.0.5", - "@fortawesome/fontawesome-free": "^5.13.1", - "@ng-bootstrap/ng-bootstrap": "^6.1.0", + "@fortawesome/fontawesome-free": "^5.14.0", + "@ng-bootstrap/ng-bootstrap": "^7.0.0", "@ngx-validate/core": "^0.0.11", - "@swimlane/ngx-datatable": "^17.0.0", + "@swimlane/ngx-datatable": "^17.1.0", "bootstrap": "^4.5.0", "chart.js": "^2.9.3", "tslib": "^2.0.0" diff --git a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts index af2db977fe..d54d219c6f 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts @@ -43,6 +43,29 @@ export const DEFAULT_ERROR_MESSAGES = { }, }; +export const DEFAULT_ERROR_LOCALIZATIONS = { + defaultError: { + title: 'AbpUi::DefaultErrorMessage', + details: 'AbpUi::DefaultErrorMessageDetail', + }, + defaultError401: { + title: 'AbpUi::DefaultErrorMessage401', + details: 'AbpUi::DefaultErrorMessage401Detail', + }, + defaultError403: { + title: 'AbpUi::DefaultErrorMessage403', + details: 'AbpUi::DefaultErrorMessage403Detail', + }, + defaultError404: { + title: 'AbpUi::DefaultErrorMessage404', + details: 'AbpUi::DefaultErrorMessage404Detail', + }, + defaultError500: { + title: 'AbpUi::500Message', + details: 'AbpUi::DefaultErrorMessage', + }, +}; + @Injectable({ providedIn: 'root' }) export class ErrorHandler { componentRef: ComponentRef; @@ -64,7 +87,10 @@ export class ErrorHandler { private listenToRouterError() { this.actions - .pipe(ofActionSuccessful(RouterError), filter(this.filterRouteErrors)) + .pipe( + ofActionSuccessful(RouterError), + filter(this.filterRouteErrors), + ) .subscribe(() => this.show404Page()); } @@ -88,7 +114,10 @@ export class ErrorHandler { filter(this.filterRestErrors), ) .subscribe(err => { - const body = snq(() => err.error.error, DEFAULT_ERROR_MESSAGES.defaultError.title); + const body = snq(() => err.error.error, { + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, + defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, + }); if (err instanceof HttpErrorResponse && err.headers.get('_AbpErrorFormat')) { const confirmation$ = this.showError(null, null, body); @@ -105,11 +134,11 @@ export class ErrorHandler { ? this.show401Page() : this.showError( { - key: 'AbpAccount::DefaultErrorMessage401', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.title, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.title, }, { - key: 'AbpAccount::DefaultErrorMessage401Detail', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.details, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.details, }, ).subscribe(() => this.navigateToLogin()); @@ -117,11 +146,11 @@ export class ErrorHandler { case 403: this.createErrorComponent({ title: { - key: 'AbpAccount::DefaultErrorMessage403', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError403.title, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError403.title, }, details: { - key: 'AbpAccount::DefaultErrorMessage403Detail', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError403.details, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError403.details, }, status: 403, @@ -132,11 +161,11 @@ export class ErrorHandler { ? this.show404Page() : this.showError( { - key: 'AbpAccount::DefaultErrorMessage404', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError404.details, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.details, }, { - key: 'AbpAccount::DefaultErrorMessage404Detail', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError404.title, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.title, }, ); @@ -144,11 +173,11 @@ export class ErrorHandler { case 500: this.createErrorComponent({ title: { - key: 'AbpAccount::500Message', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError500.title, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError500.title, }, details: { - key: 'AbpAccount::InternalServerErrorMessage', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError500.details, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError500.details, }, status: 500, @@ -158,7 +187,7 @@ export class ErrorHandler { if (err.statusText === 'Unknown Error') { this.createErrorComponent({ title: { - key: 'AbpAccount::DefaultErrorMessage', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, }, details: err.message, @@ -168,8 +197,14 @@ export class ErrorHandler { break; default: this.showError( - DEFAULT_ERROR_MESSAGES.defaultError.details, - DEFAULT_ERROR_MESSAGES.defaultError.title, + { + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.details, + defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.details, + }, + { + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, + defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, + }, ); break; } @@ -180,7 +215,7 @@ export class ErrorHandler { private show401Page() { this.createErrorComponent({ title: { - key: 'AbpAccount::401Message', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.title, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.title, }, status: 401, @@ -190,7 +225,7 @@ export class ErrorHandler { private show404Page() { this.createErrorComponent({ title: { - key: 'AbpAccount::404Message', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError404.title, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.title, }, status: 404, @@ -207,10 +242,16 @@ export class ErrorHandler { message = body.details; title = body.message; } else if (body.message) { - title = DEFAULT_ERROR_MESSAGES.defaultError.title; + title = { + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, + defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, + }; message = body.message; } else { - message = body.message || DEFAULT_ERROR_MESSAGES.defaultError.title; + message = body.message || { + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, + defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, + }; } } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts index 89e9239b6e..30dfee3e93 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts @@ -9,7 +9,7 @@ import { Actions, NgxsModule, ofActionDispatched, Store } from '@ngxs/store'; import { OAuthService } from 'angular-oauth2-oidc'; import { of } from 'rxjs'; import { HttpErrorWrapperComponent } from '../components/http-error-wrapper/http-error-wrapper.component'; -import { DEFAULT_ERROR_MESSAGES, ErrorHandler } from '../handlers'; +import { DEFAULT_ERROR_MESSAGES, ErrorHandler, DEFAULT_ERROR_LOCALIZATIONS } from '../handlers'; import { ConfirmationService } from '../services'; import { httpErrorConfigFactory } from '../tokens/http-error.token'; @@ -66,11 +66,11 @@ describe('ErrorHandler', () => { const error = new HttpErrorResponse({ status: 500 }); const params = { title: { - key: 'AbpAccount::500Message', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError500.title, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError500.title, }, details: { - key: 'AbpAccount::InternalServerErrorMessage', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError500.details, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError500.details, }, status: 500, @@ -95,11 +95,11 @@ describe('ErrorHandler', () => { const error = new HttpErrorResponse({ status: 403 }); const params = { title: { - key: 'AbpAccount::DefaultErrorMessage403', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError403.title, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError403.title, }, details: { - key: 'AbpAccount::DefaultErrorMessage403Detail', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError403.details, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError403.details, }, status: 403, @@ -124,7 +124,7 @@ describe('ErrorHandler', () => { const error = new HttpErrorResponse({ status: 0, statusText: 'Unknown Error' }); const params = { title: { - key: 'AbpAccount::DefaultErrorMessage', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, }, details: error.message, @@ -150,11 +150,11 @@ describe('ErrorHandler', () => { expect(errorConfirmation).toHaveBeenCalledWith( { - key: 'AbpAccount::DefaultErrorMessage404', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError404.details, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.details, }, { - key: 'AbpAccount::DefaultErrorMessage404Detail', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError404.title, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.title, }, CONFIRMATION_BUTTONS, @@ -165,8 +165,14 @@ describe('ErrorHandler', () => { store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 412 }))); expect(errorConfirmation).toHaveBeenCalledWith( - DEFAULT_ERROR_MESSAGES.defaultError.details, - DEFAULT_ERROR_MESSAGES.defaultError.title, + { + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.details, + defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.details, + }, + { + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, + defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, + }, CONFIRMATION_BUTTONS, ); }); @@ -176,11 +182,11 @@ describe('ErrorHandler', () => { expect(errorConfirmation).toHaveBeenCalledWith( { - key: 'AbpAccount::DefaultErrorMessage401', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.title, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.title, }, { - key: 'AbpAccount::DefaultErrorMessage401Detail', + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.details, defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.details, }, CONFIRMATION_BUTTONS, @@ -205,7 +211,10 @@ describe('ErrorHandler', () => { store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 401, headers }))); expect(errorConfirmation).toHaveBeenCalledWith( - DEFAULT_ERROR_MESSAGES.defaultError.title, + { + key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, + defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, + }, null, CONFIRMATION_BUTTONS, ); diff --git a/npm/ng-packs/scripts/publish.ts b/npm/ng-packs/scripts/publish.ts index 1514d229fc..e76af16601 100644 --- a/npm/ng-packs/scripts/publish.ts +++ b/npm/ng-packs/scripts/publish.ts @@ -8,7 +8,9 @@ program '-v, --nextVersion ', 'next semantic version. Available versions: ["major", "minor", "patch", "premajor", "preminor", "prepatch", "prerelease", "or type a custom version"]', ) - .option('-p, --preview', 'publish with preview tag'); + .option('-p, --preview', 'publishes with preview tag') + .option('-r, --rc', 'publishes with next tag') + .option('-g, --skipGit', 'skips git push'); program.parse(process.argv); @@ -55,14 +57,13 @@ const publish = async () => { await fse.rename('../lerna.publish.json', '../lerna.json'); + let tag: string; + if (program.preview) tag = 'preview'; + if (program.rc) tag = 'next'; + await execa( 'yarn', - [ - 'lerna', - 'exec', - '--', - `"npm publish --registry ${registry}${program.preview ? ' --tag preview' : ''}"`, - ], + ['lerna', 'exec', '--', `"npm publish --registry ${registry}${tag ? ` --tag ${tag}` : ''}"`], { stdout: 'inherit', cwd: '../', @@ -71,7 +72,7 @@ const publish = async () => { await fse.rename('../lerna.json', '../lerna.publish.json'); - if (!program.preview) { + if (!program.preview && !program.skipGit) { await execa('git', ['add', '../packages/*', '../package.json', '../lerna.version.json'], { stdout: 'inherit', }); diff --git a/npm/ng-packs/yarn.lock b/npm/ng-packs/yarn.lock index af54b9730b..ffaf8fcb65 100644 --- a/npm/ng-packs/yarn.lock +++ b/npm/ng-packs/yarn.lock @@ -98,41 +98,33 @@ dependencies: just-compare "^1.3.0" -"@angular-builders/jest@^9.0.1": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@angular-builders/jest/-/jest-9.0.1.tgz#e2fcdce8f9ad3060eb8a19bdb17dc47075b8eec5" - integrity sha512-dHbAKxPbRMNZ9a3t2jeuyOKRA7HxYNlDf54xU6CIDnJP6w/PqefKUCLfjkHCbPKtau1ttu6pc+cXYzpp833G3w== +"@angular-builders/jest@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@angular-builders/jest/-/jest-10.0.0.tgz#1d4f80c15544a2a13de9e335b0d2d8f076026388" + integrity sha512-TrREFfoD5YigvUyu4P1UrolTVtIHqBMPDWOCUVcgpx6GyEQPmQNBLm+GJbybxC2Ks8OUmCma9aWLeVIJ9isrbA== dependencies: - "@angular-devkit/architect" ">=0.900.0 < 0.1000.0" - "@angular-devkit/core" "^9.0.0" - jest-preset-angular "^8.1.2" - lodash "^4.17.10" + "@angular-devkit/architect" ">=0.1000.0 < 0.1100.0" + "@angular-devkit/core" "^10.0.0" + jest-preset-angular "^8.2.1" + lodash "^4.17.15" -"@angular-devkit/architect@0.1000.5": - version "0.1000.5" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1000.5.tgz#39488b92a366e49d50dda0af4eee3b0bb9425ec4" - integrity sha512-BhNqB7fmmrDQwxoJ3zdw5FslFD+nYGgMTiC48ERVYJVuN3BK6xtt2OGA7vho3eJ13OE5cPGUi8crQDRROgxxQg== +"@angular-devkit/architect@0.1000.6", "@angular-devkit/architect@>=0.1000.0 < 0.1100.0": + version "0.1000.6" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1000.6.tgz#d8143abbf1a1cef8e0ea9c80690821e8ca4cd54c" + integrity sha512-IZ8yiiW+LQ5mI3VbNHzisTIn0j6D1inQZgcZtc5W2A7fFNvBlIh6vGU3mB6Qvg678Gt6tlvnNT6/R9A9Ct7VnA== dependencies: - "@angular-devkit/core" "10.0.5" + "@angular-devkit/core" "10.0.6" rxjs "6.5.5" -"@angular-devkit/architect@>=0.900.0 < 0.1000.0": - version "0.901.12" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.901.12.tgz#e0f78b7282f851d27af6802dc7a261022421b2d5" - integrity sha512-gLlsxa+3JPV1m1gRvRMujOs4xKox6I5BkYmOD1zfu+dB6y3LuBAvHfXA6FaTDVOMBrmSlWnE4PmOmB6xd7wxMA== - dependencies: - "@angular-devkit/core" "9.1.12" - rxjs "6.5.4" - -"@angular-devkit/build-angular@~0.1000.0": - version "0.1000.5" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1000.5.tgz#9be7c63d96d3adf7d6929a09618df9c57d61f76e" - integrity sha512-YOlYdOksFsirCV7l2S0T/k9phRtT5eR+Pk3uWHXGfrkCBzFlAHw1EqOlJLOOSuN+stGHHYnKES2WUVJRJXLlCQ== +"@angular-devkit/build-angular@~0.1000.6": + version "0.1000.6" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1000.6.tgz#7c4a8a4792f7252fe8d0bd57b46e86f44c93d4b3" + integrity sha512-tKyVD8Wqfo2wFdfWmc7OMzFn30Zl37heEusnMrQD5/zZ3Hw4Nqt2kf3pf3hbWl1GExUVFYyRNoCOCh9DaIfh0w== dependencies: - "@angular-devkit/architect" "0.1000.5" - "@angular-devkit/build-optimizer" "0.1000.5" - "@angular-devkit/build-webpack" "0.1000.5" - "@angular-devkit/core" "10.0.5" + "@angular-devkit/architect" "0.1000.6" + "@angular-devkit/build-optimizer" "0.1000.6" + "@angular-devkit/build-webpack" "0.1000.6" + "@angular-devkit/core" "10.0.6" "@babel/core" "7.9.6" "@babel/generator" "7.9.6" "@babel/plugin-transform-runtime" "7.9.6" @@ -140,7 +132,7 @@ "@babel/runtime" "7.9.6" "@babel/template" "7.8.6" "@jsdevtools/coverage-istanbul-loader" "3.0.3" - "@ngtools/webpack" "10.0.5" + "@ngtools/webpack" "10.0.6" ajv "6.12.3" autoprefixer "9.8.0" babel-loader "8.1.0" @@ -195,37 +187,37 @@ webpack-subresource-integrity "1.4.1" worker-plugin "4.0.3" -"@angular-devkit/build-ng-packagr@~0.1000.0": - version "0.1000.5" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.1000.5.tgz#681774fa8982bf092e7ee33a19842e531a36a4dc" - integrity sha512-MiYwlyz6LOQONEBZMiaN3U7/CpsttNKmphdMmsBZyIPBnlHIHeHgekUEjYSMJDI9C/31HsstVR2DIJjOD2nJmw== +"@angular-devkit/build-ng-packagr@~0.1000.6": + version "0.1000.6" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.1000.6.tgz#08fe3a0e140cd4be01949449c443a4e02fb68846" + integrity sha512-agE6Dw8EZmKw+vgdiJOpbrDX7JDX8jYFXVxkeCpkl3QIQSx3pDjBoggwV4NDo9mT8/R/i1WD5idFrrm+vbI/0w== dependencies: - "@angular-devkit/architect" "0.1000.5" + "@angular-devkit/architect" "0.1000.6" rxjs "6.5.5" -"@angular-devkit/build-optimizer@0.1000.5": - version "0.1000.5" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1000.5.tgz#dc1e6fa38b7ede190ab1f31867011e624fde83ed" - integrity sha512-H6jTF7TOYq909HR6M3Bu8lScqrG1qznK2XMefK3m1I1jBJ+WO4bL7hrhTp/CIY3d+47txep+XqhitDWD/6ikpg== +"@angular-devkit/build-optimizer@0.1000.6": + version "0.1000.6" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1000.6.tgz#f5b208be155b0ffb37d7380fc1a0e12a3765319d" + integrity sha512-R8zDEAvd9PeUKvOKh6I7xp3w+MViCwjGKoOZcznjH/i/9PQjOHCMwU5S48RQloQjMGu96eDMUGOVnd9qkzXUEw== dependencies: loader-utils "2.0.0" source-map "0.7.3" tslib "2.0.0" webpack-sources "1.4.3" -"@angular-devkit/build-webpack@0.1000.5": - version "0.1000.5" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1000.5.tgz#040f58661cb81e545d2e2872ebb0b55ab695e27d" - integrity sha512-233EbW+6IKJEV20IG9Qzlz8MdDedzoTheB+lx9tTPn21m0ph2vmpTsNOsmMWwvdBBgXbv3pW3SMbJiqDCh7u4w== +"@angular-devkit/build-webpack@0.1000.6": + version "0.1000.6" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1000.6.tgz#1ec6942b4079b6cb6704b5d39af7df14102d562e" + integrity sha512-R01bJWuvckU5IdjcqoCeikLBpHRqt5fgfD0a4Hsg3evqW6xxXcSgc+YhWfeEmyU/nF/kVel8G2bFyPzhZP4QdQ== dependencies: - "@angular-devkit/architect" "0.1000.5" - "@angular-devkit/core" "10.0.5" + "@angular-devkit/architect" "0.1000.6" + "@angular-devkit/core" "10.0.6" rxjs "6.5.5" -"@angular-devkit/core@10.0.5": - version "10.0.5" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.0.5.tgz#ad747edbf3344a87f86acb71bdeaac0380538c04" - integrity sha512-ZaF1EYniIo+yRLvvaKnLrjYCdKeg2U3YtbA932M2AIfjgfFv/9mnAbH4jmZsBZhCstxZPz+ItwJesdR20h4Z1Q== +"@angular-devkit/core@10.0.6", "@angular-devkit/core@^10.0.0": + version "10.0.6" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.0.6.tgz#cbb40a34f976f9496270efc4fbdb3ad836b9723e" + integrity sha512-mVvqSEoeErZ7bAModk95EAa6R9Nl23rvX+/TXuKVTK2dziMFBOrwHjb1DYhnZxFIH4xfUftCx+BWHjXBXCPYlA== dependencies: ajv "6.12.3" fast-json-stable-stringify "2.1.0" @@ -244,23 +236,12 @@ rxjs "6.4.0" source-map "0.7.3" -"@angular-devkit/core@9.1.12", "@angular-devkit/core@^9.0.0": - version "9.1.12" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.1.12.tgz#7cebce51918fe7f3462d8c58c9fbd5a3e2c3b3e7" - integrity sha512-D/GnBeSlmdgGn7EhuE32HuPuRAjvUuxi7Q6WywBI8PSsXKAGnrypghBwMATNnOA24//CgbW2533Y9VWHaeXdeA== - dependencies: - ajv "6.12.3" - fast-json-stable-stringify "2.1.0" - magic-string "0.25.7" - rxjs "6.5.4" - source-map "0.7.3" - -"@angular-devkit/schematics@10.0.5": - version "10.0.5" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-10.0.5.tgz#a9365e06a433c89f0cf7e0a0937ab17816206b1e" - integrity sha512-UeeV8O0IydxCKj2oejygs80oIAQXPYrJHo0PQ/fa0RH6rtV3wZwirdy+yqq/BDGhtV/5VQSVCvu6uyAtGGMgNg== +"@angular-devkit/schematics@10.0.6": + version "10.0.6" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-10.0.6.tgz#dc3486448cc34544f7076f7fe0a67b75137ae840" + integrity sha512-V3T4cf+jVKiPYyBrSVHf3ZSnk4wIc1WEaaeFta56HccEGQCQpvAFKqDurmtMHer50Hhaxhn7IC3Oi5kPnvkNyQ== dependencies: - "@angular-devkit/core" "10.0.5" + "@angular-devkit/core" "10.0.6" ora "4.0.4" rxjs "6.5.5" @@ -272,10 +253,10 @@ "@angular-devkit/core" "8.3.29" rxjs "6.4.0" -"@angular/animations@~10.0.0": - version "10.0.8" - resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-10.0.8.tgz#f033a7043f41075fae128ea6e91bcdaec3a4df2f" - integrity sha512-vxJRNz6CbguuoLxZpKbaCRcJRFg+/UeXvrUjRlUxwJpAni5hFYkWZUjjXvHDszI97q8FkDNLpGDPcggGqPfo4w== +"@angular/animations@~10.0.10": + version "10.0.10" + resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-10.0.10.tgz#c4f872e2917e9e7d4ffd04a64539e2aae67eef0c" + integrity sha512-lIbNeLVVl9bO41orPFpKoobCvxZIZ2wdcKJBEFtQiOdw0khRQQ8k7so4TAWOZXRJR+MkOUCjU2pO8gbMXgBweQ== dependencies: tslib "^2.0.0" @@ -286,16 +267,16 @@ optionalDependencies: parse5 "^5.0.0" -"@angular/cli@~10.0.0": - version "10.0.5" - resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-10.0.5.tgz#04628246ee6e5428c124ea14656fb1b40f819408" - integrity sha512-Qsa90IGPApJS9HL6Ih7Z8S8mIdriWYEintsT9cfI0WMVYyjvSNZQBf1SvnNU3IEboV4RFnpfXJNmQz1Q4KsRFQ== +"@angular/cli@~10.0.6": + version "10.0.6" + resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-10.0.6.tgz#366114e36a155611b553be535bec166bd28b4119" + integrity sha512-gQbQA/CsCyMf9RKEv1hJBCdBebV2BHeT4lGi56Eii0IkvZD5WIH0dNfQzR+6ErqGDgE1EI+9YCuX3psMEvCRUA== dependencies: - "@angular-devkit/architect" "0.1000.5" - "@angular-devkit/core" "10.0.5" - "@angular-devkit/schematics" "10.0.5" - "@schematics/angular" "10.0.5" - "@schematics/update" "0.1000.5" + "@angular-devkit/architect" "0.1000.6" + "@angular-devkit/core" "10.0.6" + "@angular-devkit/schematics" "10.0.6" + "@schematics/angular" "10.0.6" + "@schematics/update" "0.1000.6" "@yarnpkg/lockfile" "1.1.0" ansi-colors "4.1.1" debug "4.1.1" @@ -312,10 +293,10 @@ universal-analytics "0.4.20" uuid "8.1.0" -"@angular/common@~10.0.0": - version "10.0.8" - resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.0.8.tgz#591263b42f8d03f3730e927251dae175e0d60669" - integrity sha512-Nzk5ckQ8y/qvTmqRdzpwUQELYD9N6DJC5yPWQw+remlkUTw24KX4KMUnt9Iy/2eFQC65MhZoTwostuKpjffrlQ== +"@angular/common@~10.0.10": + version "10.0.10" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.0.10.tgz#405c3640449bbf9f2c3c764be93a91749cb931d4" + integrity sha512-p6/pTk0s0Ai5uUkOHHFZwp+TjxRNPldPxTU2LVxg2xuBEQTO53BsfBKn3zi74epdb1kBC0Yjdj6yEL4dITBs7A== dependencies: tslib "^2.0.0" @@ -345,29 +326,29 @@ dependencies: tslib "^2.0.0" -"@angular/core@~10.0.0": - version "10.0.8" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-10.0.8.tgz#dd8855e9faa3f2ba37268144d15402d346b1be57" - integrity sha512-52M1krR/TRZsV9WKPd+r7IPVT8c5Nh+Im1z3/ZY7rG0HmxXsV7YzuTuKV7oyHbWPg0WPJAwyH0+qxBK3kpvc8w== +"@angular/core@~10.0.10": + version "10.0.10" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-10.0.10.tgz#10ca31d55899992e0b1585cfef4df8c40a1c60ac" + integrity sha512-PIQhLqjZayVXJoXs4WQu7orkePqFiux19y7bgBrsSAithe+g9BkrSIdX7+tkkX0zggUWKywY92YuMZCJ/S+uiw== dependencies: tslib "^2.0.0" -"@angular/forms@~10.0.0": - version "10.0.8" - resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-10.0.8.tgz#e491010a78be6997d4ac228565de661f5505dc2f" - integrity sha512-7aEF0iO8pwcCtVgY0uNN7njVvdXWzXCTygd0SfqOTJRpCQwZ5IgICmUkpjlo8soTrOUC/8VRUstEKVltNzj9gA== +"@angular/forms@~10.0.10": + version "10.0.10" + resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-10.0.10.tgz#c9ed1728be678209beaae89f9aa257357764fb36" + integrity sha512-bWjbsqMTiCNQZzXAfiEwT/tiAzSvChnqBimrJWNSHVYRkp71TkDcKXn6mA+E//YR0eZ84GKNNiVlKFxqkmeyqQ== dependencies: tslib "^2.0.0" -"@angular/language-service@~10.0.0": - version "10.0.8" - resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-10.0.8.tgz#2566f5ae48c54134c3f8a22c4f40ac4e2e039436" - integrity sha512-RO8KjO9yLrVaIxoArLPuO+Q63gNorJ6C8lerLBNIUvpAzf2Pa1wUOZ/Vrt7lBZADC9/FfYfGhIihFBZuZOVmpA== +"@angular/language-service@~10.0.10": + version "10.0.10" + resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-10.0.10.tgz#ef01bcd48b8ce51b57c755e75c725b2b6ae43d2f" + integrity sha512-Id3hmPR5UeWAKsDeB6+O7m+4+FpUr5zcYdM8xXAzTjjn8laSuDUMb17wsAVRntPgRJv4SeaqUbWevwSkM5KExA== -"@angular/localize@~10.0.0": - version "10.0.8" - resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-10.0.8.tgz#167a1fed27ea11883247acfcfbdc264033172014" - integrity sha512-vtGLYHM1eguWqTNm037x15I24gx3YVbv6mbjG7AFh7nLRpXSn3wxiSSNlYzDJs0lne3qY4RBn3zSeMfRm8W+mg== +"@angular/localize@~10.0.10": + version "10.0.10" + resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-10.0.10.tgz#42f981685d4bbd949ac6e6066a142e45331d65da" + integrity sha512-5zIbAqcwzHPqOg/LK7G8WJ2cNEl8hpmObcsqiw3IVyD831htnwWglfkgcR+dJvPWiYRxTGnxe2fvEnIhFpZOXQ== dependencies: "@babel/core" "7.8.3" glob "7.1.2" @@ -382,24 +363,24 @@ glob "7.1.2" yargs "15.3.0" -"@angular/platform-browser-dynamic@~10.0.0": - version "10.0.8" - resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.0.8.tgz#dd865d535a324a59a334a6f973bcd2d6333c2191" - integrity sha512-1hIourV0jF8188xwrweQnmP7QmcoRZu8D2wcy4EYyFU+i0mF6JXxJ97rfqVD3ueDr+Sd9VqXGZYFb0mO5G6MNg== +"@angular/platform-browser-dynamic@~10.0.10": + version "10.0.10" + resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.0.10.tgz#0ae069a67c3fa7d593af554a333dd2b69d12903c" + integrity sha512-6jbn0Ldyc+80BCETGtE7pzfKlbjfa/wEPhLEGWoYtxrrJ5UB3CblGpDMOsv1ibOQijPZ/JSmIMmAxz66+pLx3g== dependencies: tslib "^2.0.0" -"@angular/platform-browser@~10.0.0": - version "10.0.8" - resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-10.0.8.tgz#9d9880f090664fc1908f42e4be0a899d435681c8" - integrity sha512-x6ZJz6K+YvsgZFTTD5Rv/uwJAGVWzCqvYUspq25MjvkYTFAEOc6kQ9cfsqS/dTqhPBDoTTjGu6qhIxgm4ovGjA== +"@angular/platform-browser@~10.0.10": + version "10.0.10" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-10.0.10.tgz#13ea3e32fb798c6d10148d38f1c9589a26f74e6d" + integrity sha512-srNGkvg9177skff7QOe3L+nGOSbrKLzFt3Z5O3oM0N0TWr8QlWEA+zQm8n0zLHI8AmdZbmFzAYYJiBvVCSc5RQ== dependencies: tslib "^2.0.0" -"@angular/router@~10.0.0": - version "10.0.8" - resolved "https://registry.yarnpkg.com/@angular/router/-/router-10.0.8.tgz#8eec22a8c1050be4fb4003d177bee9e566df8aaf" - integrity sha512-Plub5gtfRv0Uk/e1Q66LVXRohzfMPqE9a4OqboFDjlWT5a94XSJaSHMgQHZ0esKoRidxijo3aRJOUSc/LAO9JQ== +"@angular/router@~10.0.10": + version "10.0.10" + resolved "https://registry.yarnpkg.com/@angular/router/-/router-10.0.10.tgz#fee0fccbe00c3f4a29ddeef9fe1bcbcb8b7ad28c" + integrity sha512-wDmr/Spuv4OhPK5a49AvgJhaedRw4yb7nmPMd51sWqzOV31RRcGXORjiXZOcSpElLxM9f7JV0tWDR5p5ko/kPA== dependencies: tslib "^2.0.0" @@ -715,9 +696,9 @@ js-tokens "^4.0.0" "@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.11.0", "@babel/parser@^7.11.1", "@babel/parser@^7.8.3", "@babel/parser@^7.8.6", "@babel/parser@^7.9.6": - version "7.11.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.2.tgz#0882ab8a455df3065ea2dcb4c753b2460a24bead" - integrity sha512-Vuj/+7vLo6l1Vi7uuO+1ngCDNeVmNbTngcJFKCR/oEtz8tKz0CJxZEGmPt9KcIloZhOZ3Zit6xbpXT2MDlS9Vw== + version "7.11.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.3.tgz#9e1eae46738bcd08e23e867bab43e7b95299a8f9" + integrity sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA== "@babel/plugin-proposal-async-generator-functions@^7.8.3": version "7.10.5" @@ -1285,38 +1266,38 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@commitlint/execute-rule@^9.1.1": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-9.1.1.tgz#59bfe018d85545881e4bd1a47f9cd155b2df4c2c" - integrity sha512-kCncHMXfVDfmUx1NExl7T+s7udAWEOh039DFGR9R5MWoy+zm2cJsCdsbWFFuNbcPWCKor57ywdIUN2t048P6Yg== +"@commitlint/execute-rule@^9.1.2": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-9.1.2.tgz#74a77eae50c8d2e5766822061ddf0df1b4f08027" + integrity sha512-NGbeo0KCVYo1yj9vVPFHv6RGFpIF6wcQxpFYUKGIzZVV9Vz1WyiKS689JXa99Dt1aN0cZlEJJLnTNDIgYls0Vg== "@commitlint/load@>6.1.1": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-9.1.1.tgz#8b1580ab821729bbe197e95f960e7cee966402ae" - integrity sha512-RCIX44M8nq3bW98vuxA6C2rM0wAnZlneFls2OVi5l2BvD/rsQ2R9CZYwPt/MYU6RbwMmlGhkuHdScwK6pJeUqA== + version "9.1.2" + resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-9.1.2.tgz#c79634e8805ab35f318c535fdbda748288bf5395" + integrity sha512-FPL82xBuF7J3EJ57kLVoligQP4BFRwrknooP+vNT787AXmQ/Fddc/iYYwHwy67pNkk5N++/51UyDl/CqiHb6nA== dependencies: - "@commitlint/execute-rule" "^9.1.1" - "@commitlint/resolve-extends" "^9.1.1" - "@commitlint/types" "^9.1.1" + "@commitlint/execute-rule" "^9.1.2" + "@commitlint/resolve-extends" "^9.1.2" + "@commitlint/types" "^9.1.2" chalk "4.1.0" cosmiconfig "^6.0.0" - lodash "^4.17.15" + lodash "^4.17.19" resolve-from "^5.0.0" -"@commitlint/resolve-extends@^9.1.1": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-9.1.1.tgz#8ce703fcd2426ab1f5449906442267e18c995430" - integrity sha512-IxxNaSUT+dPhSCQPAMpQyHQlcSVikDxQWJFP6uwGJUU5rQff+0yQTuC3taWI2NWaATukjNWboAbH2vgBDnrDrg== +"@commitlint/resolve-extends@^9.1.2": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-9.1.2.tgz#222dcb73b139b6645cf3ce3bd55db429a98600b3" + integrity sha512-HcoL+qFGmWEu9VM4fY0HI+VzF4yHcg3x+9Hx6pYFZ+r2wLbnKs964y0v68oyMO/mS/46MVoLNXZGR8U3adpadg== dependencies: import-fresh "^3.0.0" - lodash "^4.17.15" + lodash "^4.17.19" resolve-from "^5.0.0" resolve-global "^1.0.0" -"@commitlint/types@^9.1.1": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-9.1.1.tgz#d97ebc35d20eed6d8458361cbca5c7cb85d45184" - integrity sha512-SXY8bCQ1qacJ8AUTUxjabY8G6OjSmMPLN9MBCzGaKOjpPNX6z8zbXTbk9oU3GHZLtcxweWLCi2n49IRS4iQlwg== +"@commitlint/types@^9.1.2": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-9.1.2.tgz#d05f66db03e3a3638a654e8badf2deb489eb220d" + integrity sha512-r3fwVbVH+M8W0qYlBBZFsUwKe6NT5qvz+EmU7sr8VeN1cQ63z+3cfXyTo7WGGEMEgKiT0jboNAK3b1FZp8k9LQ== "@evocateur/libnpmaccess@^3.1.2": version "3.1.2" @@ -1392,7 +1373,7 @@ unique-filename "^1.1.1" which "^1.3.1" -"@fortawesome/fontawesome-free@^5.13.0", "@fortawesome/fontawesome-free@^5.13.1": +"@fortawesome/fontawesome-free@^5.13.1", "@fortawesome/fontawesome-free@^5.14.0": version "5.14.0" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.14.0.tgz#a371e91029ebf265015e64f81bfbf7d228c9681f" integrity sha512-OfdMsF+ZQgdKHP9jUbmDcRrP0eX90XXrsXIdyjLbkmSBzmMXPABB8eobUJtivaupucYaByz6WNe1PI1JuYm3qA== @@ -1590,13 +1571,13 @@ "@types/yargs" "^15.0.0" chalk "^3.0.0" -"@jest/types@^26.2.0": - version "26.2.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.2.0.tgz#b28ca1fb517a4eb48c0addea7fcd9edc4ab45721" - integrity sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA== +"@jest/types@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.3.0.tgz#97627bf4bdb72c55346eef98e3b3f7ddc4941f71" + integrity sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" + "@types/istanbul-reports" "^3.0.0" "@types/node" "*" "@types/yargs" "^15.0.0" chalk "^4.0.0" @@ -2310,7 +2291,14 @@ resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-6.2.0.tgz#0506d612ca6002bd8fa398d006fa2641013e11d4" integrity sha512-wqwhnJFyEwvzWQJjXrEt+7oBTSvu2qPbdYvUFYhDVzOJLWB5M7YEhDAkMrfHQJ0pZNBMGr580FqYue+XiURY0Q== -"@ngneat/spectator@^5.11.0": +"@ng-bootstrap/ng-bootstrap@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-7.0.0.tgz#3bfa62eb52fdb891b1ce693ea11c39127e2d1ab7" + integrity sha512-SxUaptGWJmCxM0d2Zy1mx7K7p/YBwGZ69NmmBQVY4BE6p5av0hWrVmv9rzzfBz0rhxU7RPZLor2Jpaoq8Xyl4w== + dependencies: + tslib "^2.0.0" + +"@ngneat/spectator@^5.13.0": version "5.13.0" resolved "https://registry.yarnpkg.com/@ngneat/spectator/-/spectator-5.13.0.tgz#0a9720c3d3b60571c3619054f9831856997acf67" integrity sha512-BlCmmgHMPtrdJECwctFmziRhHzSF3xxRnKiR/qJIerCd4csgpwGbhtEEh/2jnFEyLP6yLo4T4nCslwq3dHdFrQ== @@ -2319,12 +2307,12 @@ jquery "3.5.0" replace-in-file "^4.1.3" -"@ngtools/webpack@10.0.5": - version "10.0.5" - resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.0.5.tgz#8123647aa73463e586ac5792d16751a36d5caaaa" - integrity sha512-jMHfLXFzmgRlZbHgS71BEIKDyHbkfKrJL1sWEs3Ylox+TEIYzJiGfnem5ZGpr8wplz8HMMJ+xngtABePvc3/0w== +"@ngtools/webpack@10.0.6": + version "10.0.6" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.0.6.tgz#6fe392ecad9f2869fc68eda7f1344ad0b68a8898" + integrity sha512-AbSDhPmsljkZO2jHFpge/5AHLQIrbscWgo4brrhF7NQ5TvPgE0Xn0wU7gxB9++hVUKQLGnnbAvewJyB/uYb9Nw== dependencies: - "@angular-devkit/core" "10.0.5" + "@angular-devkit/core" "10.0.6" enhanced-resolve "4.1.1" rxjs "6.5.5" webpack-sources "1.4.3" @@ -2514,24 +2502,24 @@ "@types/node" ">= 8" "@octokit/types@^5.0.0", "@octokit/types@^5.0.1": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-5.2.1.tgz#c212f03b0492faf215fa2ae506d5ec18038c2a36" - integrity sha512-PugtgEw8u++zAyBpDpSkR8K1OsT2l8QWp3ECL6bZHFoq9PfHDoKeGFWSuX2Z+Ghy93k1fkKf8tsmqNBv+8dEfQ== + version "5.4.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-5.4.0.tgz#25f2f8e24fec09214553168c41c06383c9d0f529" + integrity sha512-D/uotqF69M50OIlwMqgyIg9PuLT2daOiBAYF0P40I2ekFA2ESwwBY5dxZe/UhXdPvIbNKDzuZmQrO7rMpuFbcg== dependencies: "@types/node" ">= 8" -"@rollup/plugin-commonjs@^14.0.0": - version "14.0.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-14.0.0.tgz#4285f9ec2db686a31129e5a2b415c94aa1f836f0" - integrity sha512-+PSmD9ePwTAeU106i9FRdc+Zb3XUWyW26mo5Atr2mk82hor8+nPwkztEjFo8/B1fJKfaQDg9aM2bzQkjhi7zOw== +"@rollup/plugin-commonjs@^15.0.0": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-15.0.0.tgz#690d15a9d54ba829db93555bff9b98ff34e08574" + integrity sha512-8uAdikHqVyrT32w1zB9VhW6uGwGjhKgnDNP4pQJsjdnyF4FgCj6/bmv24c7v2CuKhq32CcyCwRzMPEElaKkn0w== dependencies: - "@rollup/pluginutils" "^3.0.8" + "@rollup/pluginutils" "^3.1.0" commondir "^1.0.1" - estree-walker "^1.0.1" - glob "^7.1.2" - is-reference "^1.1.2" - magic-string "^0.25.2" - resolve "^1.11.0" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" "@rollup/plugin-json@^4.0.0": version "4.1.0" @@ -2540,15 +2528,14 @@ dependencies: "@rollup/pluginutils" "^3.0.8" -"@rollup/plugin-node-resolve@^8.0.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz#261d79a680e9dc3d86761c14462f24126ba83575" - integrity sha512-LFqKdRLn0ShtQyf6SBYO69bGE1upV6wUhBX0vFOUnLAyzx5cwp8svA0eHUnu8+YU57XOkrMtfG63QOpQx25pHQ== +"@rollup/plugin-node-resolve@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-9.0.0.tgz#39bd0034ce9126b39c1699695f440b4b7d2b62e6" + integrity sha512-gPz+utFHLRrd41WMP13Jq5mqqzHL3OXrfj3/MkSyB6UBIcuNt9j60GCbarzMzdf1VHFpOxfQh/ez7wyadLMqkg== dependencies: "@rollup/pluginutils" "^3.1.0" "@types/resolve" "1.17.1" builtin-modules "^3.1.0" - deep-freeze "^0.0.1" deepmerge "^4.2.2" is-module "^1.0.0" resolve "^1.17.0" @@ -2562,21 +2549,21 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@schematics/angular@10.0.5", "@schematics/angular@~10.0.5": - version "10.0.5" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-10.0.5.tgz#905f7c58547fdf9847fb004c1689bc0af7a09a7a" - integrity sha512-zg8QxgW3uLva/MSKMRYfV7dzj00SUki4nxYN4j1rw42VlwNPnFrPtzFVEilL6N7wFgoHP/6cZRgm4bfXYvLBvg== +"@schematics/angular@10.0.6", "@schematics/angular@~10.0.5": + version "10.0.6" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-10.0.6.tgz#568590574dca1556280a0a04f625f79bee34ee4a" + integrity sha512-TPBpo0GnMJLvKE6rYZDkSy9pnkMH55rSJ6nfLDpQ5zzmhoD/QnASUr8trfTFs3+MqmPlX61xI00+HmStmI8sJQ== dependencies: - "@angular-devkit/core" "10.0.5" - "@angular-devkit/schematics" "10.0.5" + "@angular-devkit/core" "10.0.6" + "@angular-devkit/schematics" "10.0.6" -"@schematics/update@0.1000.5": - version "0.1000.5" - resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1000.5.tgz#7ce1012ee0b5fb88b149f84294eb49b911b01a3f" - integrity sha512-xodvq3X4B90u8myMEp9ESPnD2aC4YtNXj1FOcJ+BnguRA7q9rq9EL9Xqdef8sx3PObbSiKC0OFLyxgw76WuC3Q== +"@schematics/update@0.1000.6": + version "0.1000.6" + resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1000.6.tgz#f0131d4b3ed879ae8f19c032d310bece79691738" + integrity sha512-GGfPGPjRF/MA4EeJ+h1ebzoYDzChF4BV7SaTfpT107LPCD3McRjKS39Jw2qH/ArGNSbrbJ8fYNOIj3g/uh1GoA== dependencies: - "@angular-devkit/core" "10.0.5" - "@angular-devkit/schematics" "10.0.5" + "@angular-devkit/core" "10.0.6" + "@angular-devkit/schematics" "10.0.6" "@yarnpkg/lockfile" "1.1.0" ini "1.3.5" npm-package-arg "^8.0.0" @@ -2607,7 +2594,7 @@ dependencies: type-detect "4.0.8" -"@swimlane/ngx-datatable@^17.0.0": +"@swimlane/ngx-datatable@^17.0.0", "@swimlane/ngx-datatable@^17.1.0": version "17.1.0" resolved "https://registry.yarnpkg.com/@swimlane/ngx-datatable/-/ngx-datatable-17.1.0.tgz#ef2f91c3783526e7e89ac7a62a53e7bc9e1c90d5" integrity sha512-zYUS7uNO9OJ5UQZFuuTRjlPu6vdKA+FHYLfeEs7PgSuUiDCcbl2SWoUdS/3zIoWn/qQyws767ueWiAvvWUbpEw== @@ -2736,11 +2723,26 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" +"@types/istanbul-reports@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" + integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== + dependencies: + "@types/istanbul-lib-report" "*" + "@types/jasmine@^3.3.9": version "3.5.12" resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.5.12.tgz#5c378c1545cdc7cb339cff5578f854b6d1e0a17d" integrity sha512-vJaQ58oceFao+NzpKNqLOWwHPsqA7YEhKv+mOXvYU4/qh+BfVWIxaBtL0Ck5iCS67yOkNwGkDCrzepnzIWF+7g== +"@types/jest@26.x": + version "26.0.10" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.10.tgz#8faf7e9756c033c39014ae76a7329efea00ea607" + integrity sha512-i2m0oyh8w/Lum7wWK/YOZJakYF8Mx08UaKA1CtbmFeDquVhAEdA7znacsVSf2hJ1OQ/OfVMGN90pw/AtzF8s/Q== + dependencies: + jest-diff "^25.2.1" + pretty-format "^25.2.1" + "@types/jest@^25.2.3": version "25.2.3" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.2.3.tgz#33d27e4c4716caae4eced355097a47ad363fdcaf" @@ -2772,9 +2774,9 @@ integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= "@types/node@*", "@types/node@>= 8": - version "14.0.27" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.27.tgz#a151873af5a5e851b51b3b065c9e63390a9e0eb1" - integrity sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g== + version "14.6.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.0.tgz#7d4411bf5157339337d7cff864d9ff45f177b499" + integrity sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA== "@types/node@^12.11.1": version "12.12.54" @@ -3153,7 +3155,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@6.12.3, ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3: +ajv@6.12.3: version "6.12.3" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== @@ -3163,6 +3165,16 @@ ajv@6.12.3, ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3: + version "6.12.4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" + integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -3397,24 +3409,20 @@ arrify@^1.0.0, arrify@^1.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= -arrify@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" - integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== - asap@^2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= -asn1.js@^4.0.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" - integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== dependencies: bn.js "^4.0.0" inherits "^2.0.1" minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" asn1@~0.2.3: version "0.2.4" @@ -3527,9 +3535,9 @@ aws-sign2@~0.7.0: integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2" - integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA== + version "1.10.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428" + integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA== axobject-query@2.0.2: version "2.0.2" @@ -3695,9 +3703,9 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0: integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== bn.js@^5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.2.tgz#c9686902d3c9a27729f43ab10f9d79c2004da7b0" - integrity sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA== + version "5.1.3" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" + integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== body-parser@1.19.0: version "1.19.0" @@ -4153,11 +4161,6 @@ camelcase@^4.1.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= -camelcase@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" - integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== - caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" @@ -4169,9 +4172,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001061, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001111: - version "1.0.30001112" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001112.tgz#0fffc3b934ff56ff0548c37bc9dad7d882bcf672" - integrity sha512-J05RTQlqsatidif/38aN3PGULCLrg8OYQOlJUKbeYVzC2mGZkZLIztwRlB3MtrfLmawUmjFlNJvy/uhwniIe1Q== + version "1.0.30001115" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001115.tgz#c04cd828883ba47f6f776312e0817bcc9040cfa4" + integrity sha512-NZrG0439ePYna44lJX8evHX2L7Z3/z3qjVLnHgbBb/duNEnGo348u+BQS5o4HTWcrb++100dHFrU36IesIrC1Q== canonical-path@1.0.0: version "1.0.0" @@ -4563,13 +4566,13 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= -compare-func@^1.3.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-1.3.4.tgz#6b07c4c5e8341119baf44578085bda0f4a823516" - integrity sha512-sq2sWtrqKPkEXAC8tEJA1+BqAH9GbFkGBtUOqrUX57VSfwp8xyktctk+uLoRy5eccTdxzDcVIztlYDpKs3Jv1Q== +compare-func@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" + integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== dependencies: array-ify "^1.0.0" - dot-prop "^3.0.0" + dot-prop "^5.1.0" component-emitter@^1.2.1: version "1.3.0" @@ -4680,12 +4683,12 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -conventional-changelog-angular@^5.0.10, conventional-changelog-angular@^5.0.3: - version "5.0.10" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.10.tgz#5cf7b00dd315b6a6a558223c80d5ef24ddb34205" - integrity sha512-k7RPPRs0vp8+BtPsM9uDxRl6KcgqtCJmzRD1wRtgqmhQ96g8ifBGo9O/TZBG23jqlXS/rg8BKRDELxfnQQGiaA== +conventional-changelog-angular@^5.0.11, conventional-changelog-angular@^5.0.3: + version "5.0.11" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.11.tgz#99a3ca16e4a5305e0c2c2fae3ef74fd7631fc3fb" + integrity sha512-nSLypht/1yEflhuTogC03i7DX7sOrXGsRn14g131Potqi6cbGbGEE9PSDEHKldabB6N76HiSyw9Ph+kLmC04Qw== dependencies: - compare-func "^1.3.1" + compare-func "^2.0.0" q "^1.5.1" conventional-changelog-atom@^2.0.7: @@ -4696,12 +4699,12 @@ conventional-changelog-atom@^2.0.7: q "^1.5.1" conventional-changelog-cli@^2.0.31: - version "2.0.34" - resolved "https://registry.yarnpkg.com/conventional-changelog-cli/-/conventional-changelog-cli-2.0.34.tgz#3d9da6011aaaf24f331b606ddc5087a6b811464b" - integrity sha512-HDDIhhpsMKiiAfH/mbj7wApgN7uA33Nk4hISY3/7ijlfqXc/bmP3v4o3Yialoxz0iTBibc94xi6kfTH7XIvwDw== + version "2.1.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-cli/-/conventional-changelog-cli-2.1.0.tgz#5da5be32203ca8382815afc85b7f9151115d5e97" + integrity sha512-hZ8EcpxV4LcGOZwH+U5LJQDnyA4o/uyUdmIGzmFZMB4caujavvDBo/iTgVihk0m1QKkEhJgulagrILSm1JCakA== dependencies: add-stream "^1.0.0" - conventional-changelog "^3.1.21" + conventional-changelog "^3.1.23" lodash "^4.17.15" meow "^7.0.0" tempfile "^3.0.0" @@ -4713,12 +4716,12 @@ conventional-changelog-codemirror@^2.0.7: dependencies: q "^1.5.1" -conventional-changelog-conventionalcommits@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.3.0.tgz#c4205a659f7ca9d7881f29ee78a4e7d6aeb8b3c2" - integrity sha512-oYHydvZKU+bS8LnGqTMlNrrd7769EsuEHKy4fh1oMdvvDi7fem8U+nvfresJ1IDB8K00Mn4LpiA/lR+7Gs6rgg== +conventional-changelog-conventionalcommits@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.4.0.tgz#8d96687141c9bbd725a89b95c04966d364194cd4" + integrity sha512-ybvx76jTh08tpaYrYn/yd0uJNLt5yMrb1BphDe4WBredMlvPisvMghfpnJb6RmRNcqXeuhR6LfGZGewbkRm9yA== dependencies: - compare-func "^1.3.1" + compare-func "^2.0.0" lodash "^4.17.15" q "^1.5.1" @@ -4741,19 +4744,19 @@ conventional-changelog-core@^3.1.6: read-pkg-up "^3.0.0" through2 "^3.0.0" -conventional-changelog-core@^4.1.7: - version "4.1.7" - resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.1.7.tgz#6b5cdadda4430895cc4a75a73dd8b36e322ab346" - integrity sha512-UBvSrQR2RdKbSQKh7RhueiiY4ZAIOW3+CSWdtKOwRv+KxIMNFKm1rOcGBFx0eA8AKhGkkmmacoTWJTqyz7Q0VA== +conventional-changelog-core@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.0.tgz#d8befd1e1f5126bf35a17668276cc8c244650469" + integrity sha512-8+xMvN6JvdDtPbGBqA7oRNyZD4od1h/SIzrWqHcKZjitbVXrFpozEeyn4iI4af1UwdrabQpiZMaV07fPUTGd4w== dependencies: add-stream "^1.0.0" - conventional-changelog-writer "^4.0.16" + conventional-changelog-writer "^4.0.17" conventional-commits-parser "^3.1.0" dateformat "^3.0.0" get-pkg-repo "^1.0.0" git-raw-commits "2.0.0" git-remote-origin-url "^2.0.0" - git-semver-tags "^4.0.0" + git-semver-tags "^4.1.0" lodash "^4.17.15" normalize-package-data "^2.3.5" q "^1.5.1" @@ -4790,12 +4793,12 @@ conventional-changelog-jquery@^3.0.10: dependencies: q "^1.5.1" -conventional-changelog-jshint@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.7.tgz#955a69266951cd31e8afeb3f1c55e0517fdca943" - integrity sha512-qHA8rmwUnLiIxANJbz650+NVzqDIwNtc0TcpIa0+uekbmKHttidvQ1dGximU3vEDdoJVKFgR3TXFqYuZmYy9ZQ== +conventional-changelog-jshint@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.8.tgz#3fff4df8cb46037f77b9dc3f8e354c7f99332f13" + integrity sha512-hB/iI0IiZwnZ+seYI+qEQ4b+EMQSEC8jGIvhO2Vpz1E5p8FgLz75OX8oB1xJWl+s4xBMB6f8zJr0tC/BL7YOjw== dependencies: - compare-func "^1.3.1" + compare-func "^2.0.0" q "^1.5.1" conventional-changelog-preset-loader@^2.1.1, conventional-changelog-preset-loader@^2.3.4: @@ -4803,12 +4806,12 @@ conventional-changelog-preset-loader@^2.1.1, conventional-changelog-preset-loade resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== -conventional-changelog-writer@^4.0.16, conventional-changelog-writer@^4.0.6: - version "4.0.16" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.16.tgz#ca10f2691a8ea6d3c2eb74bd35bcf40aa052dda5" - integrity sha512-jmU1sDJDZpm/dkuFxBeRXvyNcJQeKhGtVcFFkwTphUAzyYWcwz2j36Wcv+Mv2hU3tpvLMkysOPXJTLO55AUrYQ== +conventional-changelog-writer@^4.0.17, conventional-changelog-writer@^4.0.6: + version "4.0.17" + resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.17.tgz#4753aaa138bf5aa59c0b274cb5937efcd2722e21" + integrity sha512-IKQuK3bib/n032KWaSb8YlBFds+aLmzENtnKtxJy3+HqDq5kohu3g/UdNbIHeJWygfnEbZjnCKFxAW0y7ArZAw== dependencies: - compare-func "^1.3.1" + compare-func "^2.0.0" conventional-commits-filter "^2.0.6" dateformat "^3.0.0" handlebars "^4.7.6" @@ -4819,21 +4822,21 @@ conventional-changelog-writer@^4.0.16, conventional-changelog-writer@^4.0.6: split "^1.0.0" through2 "^3.0.0" -conventional-changelog@^3.1.21: - version "3.1.21" - resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-3.1.21.tgz#4a774e6bf503acfd7e4685bb750da8c0eccf1e0d" - integrity sha512-ZGecVZPEo3aC75VVE4nu85589dDhpMyqfqgUM5Myq6wfKWiNqhDJLSDMsc8qKXshZoY7dqs1hR0H/15kI/G2jQ== +conventional-changelog@^3.1.23: + version "3.1.23" + resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-3.1.23.tgz#d696408021b579a3814aba79b38729ed86478aea" + integrity sha512-sScUu2NHusjRC1dPc5p8/b3kT78OYr95/Bx7Vl8CPB8tF2mG1xei5iylDTRjONV5hTlzt+Cn/tBWrKdd299b7A== dependencies: - conventional-changelog-angular "^5.0.10" + conventional-changelog-angular "^5.0.11" conventional-changelog-atom "^2.0.7" conventional-changelog-codemirror "^2.0.7" - conventional-changelog-conventionalcommits "^4.3.0" - conventional-changelog-core "^4.1.7" + conventional-changelog-conventionalcommits "^4.4.0" + conventional-changelog-core "^4.2.0" conventional-changelog-ember "^2.0.8" conventional-changelog-eslint "^3.0.8" conventional-changelog-express "^2.0.5" conventional-changelog-jquery "^3.0.10" - conventional-changelog-jshint "^2.0.7" + conventional-changelog-jshint "^2.0.8" conventional-changelog-preset-loader "^2.3.4" conventional-commit-types@^2.0.0: @@ -5439,11 +5442,6 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-freeze@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84" - integrity sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ= - deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -5706,21 +5704,14 @@ domutils@^1.7.0: dom-serializer "0" domelementtype "1" -dot-prop@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177" - integrity sha1-G3CK8JSknJoOfbyteQq6U52sEXc= - dependencies: - is-obj "^1.0.0" - dot-prop@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" - integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== + version "4.2.1" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4" + integrity sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ== dependencies: is-obj "^1.0.0" -dot-prop@^5.2.0: +dot-prop@^5.1.0, dot-prop@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== @@ -5733,9 +5724,9 @@ duplexer3@^0.1.4: integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= duplexer@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" - integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== duplexify@^3.4.2, duplexify@^3.6.0: version "3.7.1" @@ -5761,9 +5752,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.523: - version "1.3.524" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.524.tgz#dd49646594466192de35956a5369bb20d616aa78" - integrity sha512-ZUvklIBkfXQyA6IeiEss1nfKRICcdB5afAGZAaPGaExdfrkpUu/WWVO+X7QpNnphaVMllXnAcvKnVPdyM+DCPQ== + version "1.3.534" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.534.tgz#fc7af8518dd00a5b22a24aed3f116b5d097e2330" + integrity sha512-7x2S3yUrspNHQOoPk+Eo+iHViSiJiEGPI6BpmLy1eT2KRNGCkBt/NUYqjfXLd1DpDCQp7n3+LfA1RkbG+LqTZQ== elliptic@^6.5.3: version "6.5.3" @@ -6000,6 +5991,11 @@ estree-walker@^1.0.1: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== +estree-walker@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.1.tgz#f8e030fb21cefa183b44b7ad516b747434e7a3e0" + integrity sha512-tF0hv+Yi2Ot1cwj9eYHtxC0jB9bmjacjQs6ZBTj82H8JwUywFuc+7E83NWfNMwHXZc11mjfFcVXPe9gEP4B8dg== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -6451,9 +6447,9 @@ flush-write-stream@^1.0.0: readable-stream "^2.3.6" follow-redirects@^1.0.0: - version "1.12.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.12.1.tgz#de54a6205311b93d60398ebc01cf7015682312b6" - integrity sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg== + version "1.13.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" + integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== font-awesome@^4.7.0: version "4.7.0" @@ -6642,9 +6638,9 @@ get-stream@^4.0.0, get-stream@^4.1.0: pump "^3.0.0" get-stream@^5.0.0, get-stream@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9" - integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" @@ -6687,26 +6683,26 @@ git-semver-tags@^2.0.3: meow "^4.0.0" semver "^6.0.0" -git-semver-tags@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-4.0.0.tgz#a9dd58a0dd3561a4a9898b7e9731cf441c98fc38" - integrity sha512-LajaAWLYVBff+1NVircURJFL8TQ3EMIcLAfHisWYX/nPoMwnTYfWAznQDmMujlLqoD12VtLmoSrF1sQ5MhimEQ== +git-semver-tags@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-4.1.0.tgz#0146c9bc24ee96104c99f443071c8c2d7dc848e3" + integrity sha512-TcxAGeo03HdErzKzi4fDD+xEL7gi8r2Y5YSxH6N2XYdVSV5UkBwfrt7Gqo1b+uSHCjy/sa9Y6BBBxxFLxfbhTg== dependencies: meow "^7.0.0" semver "^6.0.0" git-up@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.1.tgz#cb2ef086653640e721d2042fe3104857d89007c0" - integrity sha512-LFTZZrBlrCrGCG07/dm1aCjjpL1z9L3+5aEeI9SBhAqSc+kiA9Or1bgZhQFNppJX6h/f5McrvJt1mQXTFm6Qrw== + version "4.0.2" + resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.2.tgz#10c3d731051b366dc19d3df454bfca3f77913a7c" + integrity sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ== dependencies: is-ssh "^1.3.0" parse-url "^5.0.0" git-url-parse@^11.1.2: - version "11.1.2" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.1.2.tgz#aff1a897c36cc93699270587bea3dbcbbb95de67" - integrity sha512-gZeLVGY8QVKMIkckncX+iCq2/L8PlwncvDFKiWkBn9EtCfYDbliRTTp6qzyQ1VMdITUfq7293zDzfpjdiGASSQ== + version "11.1.3" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.1.3.tgz#03625b6fc09905e9ad1da7bb2b84be1bf9123143" + integrity sha512-GPsfwticcu52WQ+eHp0IYkAyaOASgYdtsQDIt4rUp6GbiNt1P9ddrh3O0kQB0eD4UJZszVqNT3+9Zwcg40fywA== dependencies: git-up "^4.0.0" @@ -7816,7 +7812,7 @@ is-plain-object@^4.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-4.1.1.tgz#1a14d6452cbd50790edc7fdaa0aed5a40a35ebb5" integrity sha512-5Aw8LLVsDlZsETVMhoMXzqsXwQqr/0vlnBYzIXJbYo2F4yYlhLHs+Ez7Bod7IIQKWkJbJfxrWD7pA1Dw1TKrwA== -is-reference@^1.1.2: +is-reference@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== @@ -7836,9 +7832,9 @@ is-resolvable@^1.0.0: integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== is-ssh@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.1.tgz#f349a8cadd24e65298037a522cf7520f2e81a0f3" - integrity sha512-0eRIASHZt1E68/ixClI8bp2YK2wmBPVWEismTs6M+M099jKgrzl/3E976zIbImSIob48N2/XGe9y7ZiYdImSlg== + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.2.tgz#a4b82ab63d73976fd8263cceee27f99a88bdae2b" + integrity sha512-elEw0/0c2UscLrNG+OAorbP539E3rhliKPg+hDMWN9VwrDXfYK+4PBEykDPfxlYYtQvl84TascnQyobfQLHEhQ== dependencies: protocols "^1.1.0" @@ -8211,7 +8207,7 @@ jest-pnp-resolver@^1.2.1: resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-preset-angular@^8.1.2, jest-preset-angular@^8.2.0: +jest-preset-angular@^8.2.0, jest-preset-angular@^8.2.1: version "8.2.1" resolved "https://registry.yarnpkg.com/jest-preset-angular/-/jest-preset-angular-8.2.1.tgz#eaf5f7f6e72c1f700fbdb4c3d812976325f26d4e" integrity sha512-6t7lavnIHnZWz6a03jpZ5L7phMefi6SoBIRZ/GQdyML8YgwMtbJszbhUE+dh5lzmeNbd6AJ1gfleetGy2Rg4cQ== @@ -8334,11 +8330,11 @@ jest-snapshot@^25.5.1: semver "^6.3.0" jest-util@26.x: - version "26.2.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.2.0.tgz#0597d2a27c559340957609f106c408c17c1d88ac" - integrity sha512-YmDwJxLZ1kFxpxPfhSJ0rIkiZOM0PQbRcfH0TzJOhqCisCAsI1WcmoQqO83My9xeVA2k4n+rzg2UuexVKzPpig== + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.3.0.tgz#a8974b191df30e2bf523ebbfdbaeb8efca535b3e" + integrity sha512-4zpn6bwV0+AMFN0IYhH/wnzIQzRaYVrz1A8sYnRnj4UXDXbOVtWmlaZkO9mipFqZ13okIfN87aDoJWB7VH6hcw== dependencies: - "@jest/types" "^26.2.0" + "@jest/types" "^26.3.0" "@types/node" "*" chalk "^4.0.0" graceful-fs "^4.2.4" @@ -8397,9 +8393,9 @@ jest-worker@^25.5.0: supports-color "^7.0.0" jest-worker@^26.0.0: - version "26.2.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.2.1.tgz#5d630ab93f666b53f911615bc13e662b382bd513" - integrity sha512-+XcGMMJDTeEGncRb5M5Zq9P7K4sQ1sirhjdOxsN1462h6lFo9w59bl2LVQmdGEEeU3m+maZCkS2Tcc9SfCHO4A== + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.3.0.tgz#7c8a97e4f4364b4f05ed8bca8ca0c24de091871f" + integrity sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw== dependencies: "@types/node" "*" merge-stream "^2.0.0" @@ -8892,10 +8888,10 @@ lodash@4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.2.1: - version "4.17.19" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" - integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.2.1: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== log-symbols@^2.2.0: version "2.2.0" @@ -8979,7 +8975,7 @@ magic-string@0.25.3: dependencies: sourcemap-codec "^1.4.4" -magic-string@0.25.7, magic-string@^0.25.0, magic-string@^0.25.2: +magic-string@0.25.7, magic-string@^0.25.0, magic-string@^0.25.7: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== @@ -9136,17 +9132,15 @@ meow@^4.0.0: trim-newlines "^2.0.0" meow@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/meow/-/meow-7.0.1.tgz#1ed4a0a50b3844b451369c48362eb0515f04c1dc" - integrity sha512-tBKIQqVrAHqwit0vfuFPY3LlzJYkEOFyKa3bPgxzNl6q/RtN8KQ+ALYEASYuFayzSAsjlhXj/JZ10rH85Q6TUw== + version "7.1.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-7.1.0.tgz#50ecbcdafa16f8b58fb7eb9675b933f6473b3a59" + integrity sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog== dependencies: "@types/minimist" "^1.2.0" - arrify "^2.0.1" - camelcase "^6.0.0" camelcase-keys "^6.2.2" decamelize-keys "^1.1.0" hard-rejection "^2.1.0" - minimist-options "^4.0.2" + minimist-options "4.1.0" normalize-package-data "^2.5.0" read-pkg-up "^7.0.1" redent "^3.0.0" @@ -9295,15 +9289,7 @@ minimatch@3.0.4, minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist-options@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" - integrity sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ== - dependencies: - arrify "^1.0.1" - is-plain-obj "^1.1.0" - -minimist-options@^4.0.2: +minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== @@ -9312,6 +9298,14 @@ minimist-options@^4.0.2: is-plain-obj "^1.1.0" kind-of "^6.0.3" +minimist-options@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" + integrity sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + minimist@1.2.5, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -9360,10 +9354,10 @@ minizlib@^1.2.1: dependencies: minipass "^2.9.0" -minizlib@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" - integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA== +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== dependencies: minipass "^3.0.0" yallist "^4.0.0" @@ -9545,13 +9539,13 @@ next-tick@~1.0.0: integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= ng-packagr@^10.0.0: - version "10.0.3" - resolved "https://registry.yarnpkg.com/ng-packagr/-/ng-packagr-10.0.3.tgz#ef3fa4c3dbc826695b8e12b307172e23504e1229" - integrity sha512-M10y3eeAbIaU+NvNZEeGnuoWxZis0GFj9EKetYmmsXMjHxsPqEAZ1PUkKioIo0RPXuR1N4QGW0MSegHp5kBFeg== + version "10.0.4" + resolved "https://registry.yarnpkg.com/ng-packagr/-/ng-packagr-10.0.4.tgz#8c4503d0a7b49d7622d493527717f0b5b2e0ad97" + integrity sha512-ddx8+sRCXV8FXlmBVft+G3tdHH9W11MV4au/k+ZWEijPviGiUh0mj7JKfY0CTvtpyGAorE4ZzveiSTk5oIhlBQ== dependencies: - "@rollup/plugin-commonjs" "^14.0.0" + "@rollup/plugin-commonjs" "^15.0.0" "@rollup/plugin-json" "^4.0.0" - "@rollup/plugin-node-resolve" "^8.0.0" + "@rollup/plugin-node-resolve" "^9.0.0" ajv "^6.12.3" autoprefixer "^9.6.5" browserslist "^4.7.0" @@ -9573,7 +9567,7 @@ ng-packagr@^10.0.0: rxjs "^6.5.0" sass "^1.23.0" stylus "^0.54.7" - terser "^4.3.8" + terser "^5.0.0" update-notifier "^4.0.0" ng-zorro-antd@^9.3.0: @@ -9844,9 +9838,9 @@ npm-pick-manifest@^3.0.0: semver "^5.4.1" npm-registry-fetch@^4.0.0: - version "4.0.5" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-4.0.5.tgz#cb87cf7f25bfb048d6c3ee19d115bebf93ea5bfa" - integrity sha512-yQ0/U4fYpCCqmueB2g8sc+89ckQ3eXpmU4+Yi2j5o/r0WkKvE2+Y0tK3DEILAtn2UaQTkjTHxIXe2/CSdit+/Q== + version "4.0.7" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz#57951bf6541e0246b34c9f9a38ab73607c9449d7" + integrity sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ== dependencies: JSONStream "^1.3.4" bluebird "^3.5.1" @@ -10023,9 +10017,9 @@ onetime@^2.0.0: mimic-fn "^1.0.0" onetime@^5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.1.tgz#5c8016847b0d67fcedb7eef254751cfcdc7e9418" - integrity sha512-ZpZpjcJeugQfWsfyQlshVoowIIQ1qBGSVll4rfDq6JJVO//fesjoX808hXWfBjY+ROZgpKDI5TRSRBSoJiZ8eg== + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" @@ -10315,13 +10309,12 @@ parent-module@^1.0.0: callsites "^3.0.0" parse-asn1@^5.0.0, parse-asn1@^5.1.5: - version "5.1.5" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" - integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ== + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== dependencies: - asn1.js "^4.0.0" + asn1.js "^5.2.0" browserify-aes "^1.0.0" - create-hash "^1.1.0" evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" safe-buffer "^5.1.1" @@ -10369,17 +10362,17 @@ parse-passwd@^1.0.0: integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= parse-path@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.1.tgz#0ec769704949778cb3b8eda5e994c32073a1adff" - integrity sha512-d7yhga0Oc+PwNXDvQ0Jv1BuWkLVPXcAoQ/WREgd6vNNoKYaW52KI+RdOFjI63wjkmps9yUE8VS4veP+AgpQ/hA== + version "4.0.2" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.2.tgz#ef14f0d3d77bae8dd4bc66563a4c151aac9e65aa" + integrity sha512-HSqVz6iuXSiL8C1ku5Gl1Z5cwDd9Wo0q8CoffdAghP6bz8pJa1tcMC+m4N+z6VAS8QdksnIGq1TB6EgR4vPR6w== dependencies: is-ssh "^1.3.0" protocols "^1.4.0" parse-url@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.1.tgz#99c4084fc11be14141efa41b3d117a96fcb9527f" - integrity sha512-flNUPP27r3vJpROi0/R3/2efgKkyXqnXwyP1KQ2U0SfFRgdizOdWfvrrvJg1LuOoxs7GQhmxJlq23IpQ/BkByg== + version "5.0.2" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.2.tgz#856a3be1fcdf78dc93fc8b3791f169072d898b59" + integrity sha512-Czj+GIit4cdWtxo3ISZCvLiUjErSo0iI3wJ+q9Oi3QuMYTI6OZu+7cewMWZ+C1YAnKhYTk6/TLuhIgCypLthPA== dependencies: is-ssh "^1.3.0" normalize-url "^3.3.0" @@ -10585,9 +10578,9 @@ posix-character-classes@^0.1.0: integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= postcss-calc@^7.0.1: - version "7.0.2" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.2.tgz#504efcd008ca0273120568b0792b16cdcde8aac1" - integrity sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ== + version "7.0.3" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.3.tgz#d65cca92a3c52bf27ad37a5f732e0587b74f1623" + integrity sha512-IB/EAEmZhIMEIhG7Ov4x+l47UaXOS1n2f4FBUk/aKllQhtSCxWhTzn0nJgkqN7fo/jcWySvWTSB6Syk9L+31bA== dependencies: postcss "^7.0.27" postcss-selector-parser "^6.0.2" @@ -10999,11 +10992,11 @@ pretty-format@^25.2.1, pretty-format@^25.5.0: react-is "^16.12.0" pretty-format@^26.0.0: - version "26.2.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.2.0.tgz#83ecc8d7de676ff224225055e72bd64821cec4f1" - integrity sha512-qi/8IuBu2clY9G7qCXgCdD1Bf9w+sXakdHTRToknzMtVy0g7c4MBWaZy7MfB7ndKZovRO6XRwJiAYqq+MC7SDA== + version "26.4.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.4.0.tgz#c08073f531429e9e5024049446f42ecc9f933a3b" + integrity sha512-mEEwwpCseqrUtuMbrJG4b824877pM5xald3AkilJ47Po2YLr97/siejYQHqj2oDQBeJNbu+Q0qUuekJ8F0NAPg== dependencies: - "@jest/types" "^26.2.0" + "@jest/types" "^26.3.0" ansi-regex "^5.0.0" ansi-styles "^4.0.0" react-is "^16.12.0" @@ -11052,9 +11045,9 @@ proto-list@~1.2.1: integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= protocols@^1.1.0, protocols@^1.4.0: - version "1.4.7" - resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.7.tgz#95f788a4f0e979b291ffefcf5636ad113d037d32" - integrity sha512-Fx65lf9/YDn3hUX08XUc0J8rSux36rEsyiv21ZGUC1mOyeM3lTRpZLcrm8aAolzS4itwVfm7TAPyxC2E5zd6xg== + version "1.4.8" + resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" + integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== protoduck@^5.0.1: version "5.0.1" @@ -11200,9 +11193,9 @@ querystring@0.2.0: integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= querystringify@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" - integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== quick-lru@^1.0.0: version "1.1.0" @@ -11720,7 +11713,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.17.0, resolve@^1.3.2, resolve@^1.8.1: +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.17.0, resolve@^1.3.2, resolve@^1.8.1: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== @@ -11843,9 +11836,9 @@ rollup@2.10.9: fsevents "~2.1.2" rollup@^2.8.0: - version "2.23.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.23.0.tgz#b7ab1fee0c0e60132fd0553c4df1e9cdacfada9d" - integrity sha512-vLNmZFUGVwrnqNAJ/BvuLk1MtWzu4IuoqsH9UWK5AIdO3rt8/CSiJNvPvCIvfzrbNsqKbNzPAG1V2O4eTe2XZg== + version "2.26.3" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.26.3.tgz#502c755872a4590937cfa4e8f7eb99d3bc3c4025" + integrity sha512-Mlt39/kL2rA9egcbQbaZV1SNVplGqYYhDDMcGgHPPE0tvM3R4GrB+IEdYy2QtTrdzMQx57ZcqDFf/KWWm8F+uw== optionalDependencies: fsevents "~2.1.2" @@ -11878,13 +11871,6 @@ rxjs@6.4.0: dependencies: tslib "^1.9.0" -rxjs@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" - integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== - dependencies: - tslib "^1.9.0" - rxjs@6.5.5, rxjs@~6.5.4: version "6.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" @@ -12080,7 +12066,7 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" -serialize-javascript@^3.0.0, serialize-javascript@^3.1.0: +serialize-javascript@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.1.0.tgz#8bf3a9170712664ef2561b44b691eafe399214ea" integrity sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg== @@ -12905,14 +12891,14 @@ tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: yallist "^3.0.3" tar@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39" - integrity sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg== + version "6.0.5" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f" + integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" minipass "^3.0.0" - minizlib "^2.1.0" + minizlib "^2.1.1" mkdirp "^1.0.3" yallist "^4.0.0" @@ -12975,15 +12961,15 @@ terser-webpack-plugin@3.0.1: webpack-sources "^1.4.3" terser-webpack-plugin@^1.4.3: - version "1.4.4" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.4.tgz#2c63544347324baafa9a56baaddf1634c8abfc2f" - integrity sha512-U4mACBHIegmfoEe5fdongHESNJWqsGU+W0S/9+BmYGVQDw1+c2Ow05TpMhxjPK1sRb7cuYq1BPl1e5YHJMTCqA== + version "1.4.5" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" + integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== dependencies: cacache "^12.0.2" find-cache-dir "^2.1.0" is-wsl "^1.1.0" schema-utils "^1.0.0" - serialize-javascript "^3.1.0" + serialize-javascript "^4.0.0" source-map "^0.6.1" terser "^4.1.2" webpack-sources "^1.4.0" @@ -12998,7 +12984,7 @@ terser@4.7.0: source-map "~0.6.1" source-map-support "~0.5.12" -terser@^4.1.2, terser@^4.3.8, terser@^4.6.13: +terser@^4.1.2, terser@^4.6.13: version "4.8.0" resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== @@ -13007,6 +12993,15 @@ terser@^4.1.2, terser@^4.3.8, terser@^4.6.13: source-map "~0.6.1" source-map-support "~0.5.12" +terser@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.1.0.tgz#1f4ab81c8619654fdded51f3157b001e1747281d" + integrity sha512-pwC1Jbzahz1ZPU87NQ8B3g5pKbhyJSiHih4gLH6WZiPU8mmS1IlGbB0A2Nuvkj/LCNsgIKctg6GkYwWCeTvXZQ== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -13211,10 +13206,11 @@ trim-off-newlines@^1.0.0: integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= ts-jest@^26.0.0: - version "26.1.4" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.1.4.tgz#87d41a96016a8efe4b8cc14501d3785459af6fa6" - integrity sha512-Nd7diUX6NZWfWq6FYyvcIPR/c7GbEF75fH1R6coOp3fbNzbRJBZZAn0ueVS0r8r9ral1VcrpneAFAwB3TsVS1Q== + version "26.2.0" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.2.0.tgz#7ec22faceb05ee1467fdb5265d1b33c27441f163" + integrity sha512-9+y2qwzXdAImgLSYLXAb/Rhq9+K4rbt0417b8ai987V60g2uoNWBBmMkYgutI7D8Zhu+IbCSHbBtrHxB9d7xyA== dependencies: + "@types/jest" "26.x" bs-logger "0.x" buffer-from "1.x" fast-json-stable-stringify "2.x" @@ -13246,9 +13242,9 @@ ts-pnp@^1.1.6: integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== ts-toolbelt@^6.9.9: - version "6.15.0" - resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.15.0.tgz#e1806c4904f5dc819d8dd2c48e09ccc60bfb742f" - integrity sha512-WuXbnXmVgzAGdruH7jbYf4j/0Y4anfMOD0KUU8iw6/4SbzdQVpfso4BJ1Z/t7hKLN2++gWYIseaw/ciyWKLreQ== + version "6.15.5" + resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz#cb3b43ed725cb63644782c64fbcad7d8f28c0a83" + integrity sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A== tsickle@^0.38.1: version "0.38.1" @@ -13519,9 +13515,9 @@ upath@^1.1.1, upath@^1.2.0: integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== update-notifier@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.0.tgz#4866b98c3bc5b5473c020b1250583628f9a328f3" - integrity sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew== + version "4.1.1" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.1.tgz#895fc8562bbe666179500f9f2cebac4f26323746" + integrity sha512-9y+Kds0+LoLG6yN802wVXoIfxYEwh3FlZwzMwpCZp62S2i1/Jzeqb9Eeeju3NSHccGGasfGlK5/vEHbAifYRDg== dependencies: boxen "^4.2.0" chalk "^3.0.0" @@ -13970,9 +13966,9 @@ widest-line@^3.1.0: string-width "^4.0.0" windows-release@^3.1.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.1.tgz#cb4e80385f8550f709727287bf71035e209c4ace" - integrity sha512-Pngk/RDCaI/DkuHPlGTdIkDiTAnAkyMjoQMZqRsxydNl1qGXNIoZrB7RK8g53F2tEgQBMqQJHQdYZuQEEAu54A== + version "3.3.3" + resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.3.tgz#1c10027c7225743eec6b89df160d64c2e0293999" + integrity sha512-OSOGH1QYiW5yVor9TtmXKQvt2vjQqbYS+DqmsZw+r7xDwLXEeT3JGW0ZppFmHx4diyXmxt238KFR3N9jzevBRg== dependencies: execa "^1.0.0" diff --git a/npm/package-lock.json b/npm/package-lock.json index b50344e0c5..926f7179fb 100644 --- a/npm/package-lock.json +++ b/npm/package-lock.json @@ -2209,11 +2209,9 @@ } }, "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": true + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.0.0.tgz", + "integrity": "sha512-s7EA+hDtTYNhuXkTlhqew4txMZVdszBmKWSPEMxGr8ru8JXR7bLUFIAtPhcSuFdJQ0ILMxnJi8GkQL0yvDy/YA==" }, "compare-func": { "version": "1.3.2", @@ -7411,6 +7409,13 @@ "source-map": "~0.6.1" }, "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "optional": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/npm/package.json b/npm/package.json index 3baede1175..984aeb29f9 100644 --- a/npm/package.json +++ b/npm/package.json @@ -13,6 +13,7 @@ "npm-check-updates": "^3.1.25" }, "dependencies": { + "commander": "^6.0.0", "execa": "^3.4.0", "fs-extra": "^8.1.0" } diff --git a/npm/packs/utils/.npmignore b/npm/packs/utils/.npmignore index cc44e4969c..a6626fbc02 100644 --- a/npm/packs/utils/.npmignore +++ b/npm/packs/utils/.npmignore @@ -1,3 +1,4 @@ **/* !dist/**/* -!abp.resourcemapping.js \ No newline at end of file +!abp.resourcemapping.js +!ngcc.config.js \ No newline at end of file diff --git a/npm/packs/utils/ngcc.config.js b/npm/packs/utils/ngcc.config.js new file mode 100644 index 0000000000..e3e032113f --- /dev/null +++ b/npm/packs/utils/ngcc.config.js @@ -0,0 +1,6 @@ +module.exports = { + entryPoints: { + '.': { ignore: true }, + './dist': {}, + }, +}; diff --git a/npm/packs/utils/package.json b/npm/packs/utils/package.json index 56bc5fc2cb..3b72a10f77 100644 --- a/npm/packs/utils/package.json +++ b/npm/packs/utils/package.json @@ -13,11 +13,11 @@ "access": "public" }, "main": "dist/bundles/abp-utils.umd.js", - "module": "dist/fesm5/abp-utils.js", + "module": "dist/fesm2015/abp-utils.js", + "es2015_ivy_ngcc": "__ivy_ngcc__/dist/fesm2015/abp-utils.js", "es2015": "dist/fesm2015/abp-utils.js", - "esm5": "dist/esm5/abp-utils.js", "esm2015": "dist/esm2015/abp-utils.js", - "fesm5": "dist/fesm5/abp-utils.js", + "fesm2015_ivy_ngcc": "__ivy_ngcc__/dist/fesm2015/abp-utils.js", "fesm2015": "dist/fesm2015/abp-utils.js", "typings": "dist/abp-utils.d.ts", "metadata": "dist/abp-utils.metadata.json", diff --git a/npm/preview-publish.ps1 b/npm/preview-publish.ps1 index 524c8afcaa..dc01d2ed1c 100644 --- a/npm/preview-publish.ps1 +++ b/npm/preview-publish.ps1 @@ -8,8 +8,8 @@ param( npm install -$NextVersion = $(node get-version.js) -$rootFolder = (Get-Item -Path "./" -Verbose).FullName +$NextVersion = $(node publish-utils.js --nextVersion) +$RootFolder = (Get-Item -Path "./" -Verbose).FullName if(-Not $Version) { $Version = $NextVersion; @@ -34,7 +34,7 @@ foreach ($command in $commands) { Invoke-Expression $command if($LASTEXITCODE -ne '0' -And $command -notlike '*cd *'){ Write-Host ("Process failed! " + $command) - Set-Location $rootFolder + Set-Location $RootFolder exit $LASTEXITCODE } } \ No newline at end of file diff --git a/npm/publish-utils.js b/npm/publish-utils.js new file mode 100644 index 0000000000..8d0e1d2cc2 --- /dev/null +++ b/npm/publish-utils.js @@ -0,0 +1,21 @@ +const { program } = require('commander'); +const fse = require('fs-extra'); + +program.version('0.0.1'); +program.option('-n, --nextVersion', 'version in common.props'); +program.option('-r, --rc', 'whether version is rc'); + +program.parse(process.argv); + +if (program.nextVersion) console.log(getVersion()); + +if (program.rc) console.log(getVersion().includes('rc')); + +function getVersion() { + const commonProps = fse.readFileSync('../common.props').toString(); + const versionTag = ''; + const versionEndTag = ''; + const first = commonProps.indexOf(versionTag) + versionTag.length; + const last = commonProps.indexOf(versionEndTag); + return commonProps.substring(first, last); +} diff --git a/npm/publish.ps1 b/npm/publish.ps1 index d507d00c4d..70ad011439 100644 --- a/npm/publish.ps1 +++ b/npm/publish.ps1 @@ -4,30 +4,42 @@ param( npm install -$NextVersion = $(node get-version.js) -$rootFolder = (Get-Item -Path "./" -Verbose).FullName +$NextVersion = $(node publish-utils.js --nextVersion) +$RootFolder = (Get-Item -Path "./" -Verbose).FullName -if(-Not $Version) { +if (-Not $Version) { $Version = $NextVersion; } +$NgPacksPublishCommand = "npm run publish-packages -- --nextVersion $Version --skipGit" +$PacksPublishCommand = "npm run lerna -- exec 'npm publish --registry https://registry.npmjs.org'" +$UpdateGulpCommand = "npm run update-gulp" + +$IsRc = $(node publish-utils.js --rc) -eq "true"; + +if ($IsRc) { + $NgPacksPublishCommand += " --rc" + $UpdateGulpCommand += " --rc" + $PacksPublishCommand = $PacksPublishCommand.Substring(0, $PacksPublishCommand.Length - 1) + " --tag next'" +} + $commands = ( "cd ng-packs\scripts", "npm install", - "npm run publish-packages -- --nextVersion $Version", + $NgPacksPublishCommand, "cd ../../", "npm run lerna -- version $Version --yes --no-commit-hooks --skip-git --force-publish", "npm run replace-with-tilde", - "npm run lerna -- exec 'npm publish --registry https://registry.npmjs.org'", - "npm run update-gulp" + $PacksPublishCommand, + $UpdateGulpCommand ) foreach ($command in $commands) { Write-Host $command Invoke-Expression $command - if($LASTEXITCODE -ne '0' -And $command -notlike '*cd *'){ + if ($LASTEXITCODE -ne '0' -And $command -notlike '*cd *') { Write-Host ("Process failed! " + $command) - Set-Location $rootFolder + Set-Location $RootFolder exit $LASTEXITCODE } } \ No newline at end of file diff --git a/npm/update-gulp.js b/npm/update-gulp.js index 698ec85efe..b70799e617 100644 --- a/npm/update-gulp.js +++ b/npm/update-gulp.js @@ -3,6 +3,11 @@ var path = require('path'); const childProcess = require('child_process'); const execa = require('execa'); const fse = require('fs-extra'); +const { program } = require('commander'); + +program.version('0.0.1'); +program.option('-r, --rc', 'whether version is rc'); +program.parse(process.argv); const gulp = (folderPath) => { if ( @@ -23,7 +28,11 @@ const gulp = (folderPath) => { const updatePackages = (pkgJsonPath) => { try { const result = childProcess - .execSync(`ncu "/^@abp.*$/" --packageFile ${pkgJsonPath} -u`) + .execSync( + `ncu "/^@abp.*$/" --packageFile ${pkgJsonPath} -u${ + program.rc ? ' --greatest' : '' + }` + ) .toString(); console.log('\x1b[0m', result); } catch (error) { diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index 99244d5d6c..58b8cf83cf 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -86,6 +86,7 @@ $projects = ( "framework/src/Volo.Abp.EventBus.RabbitMQ", "framework/src/Volo.Abp.Features", "framework/src/Volo.Abp.FluentValidation", + "framework/src/Volo.Abp.GlobalFeatures", "framework/src/Volo.Abp.Guids", "framework/src/Volo.Abp.HangFire", "framework/src/Volo.Abp.Http.Abstractions", diff --git a/templates/app/angular/package.json b/templates/app/angular/package.json index c40a748fb5..8e65239475 100644 --- a/templates/app/angular/package.json +++ b/templates/app/angular/package.json @@ -17,24 +17,24 @@ "@abp/ng.setting-management": "~3.0.5", "@abp/ng.tenant-management": "~3.0.5", "@abp/ng.theme.basic": "~3.0.5", - "@angular/animations": "~10.0.1", - "@angular/common": "~10.0.1", - "@angular/compiler": "~10.0.1", - "@angular/core": "~10.0.1", - "@angular/forms": "~10.0.1", - "@angular/platform-browser": "~10.0.1", - "@angular/platform-browser-dynamic": "~10.0.1", - "@angular/router": "~10.0.1", + "@angular/animations": "~10.0.10", + "@angular/common": "~10.0.10", + "@angular/compiler": "~10.0.10", + "@angular/core": "~10.0.10", + "@angular/forms": "~10.0.10", + "@angular/platform-browser": "~10.0.10", + "@angular/platform-browser-dynamic": "~10.0.10", + "@angular/router": "~10.0.10", "rxjs": "~6.5.4", "tslib": "^2.0.0", "zone.js": "~0.10.2" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.1000.0", - "@angular-devkit/build-ng-packagr": "~0.1000.0", - "@angular/cli": "~10.0.0", - "@angular/compiler-cli": "~10.0.1", - "@angular/language-service": "~10.0.1", + "@angular-devkit/build-angular": "~0.1000.6", + "@angular-devkit/build-ng-packagr": "~0.1000.6", + "@angular/cli": "~10.0.6", + "@angular/compiler-cli": "~10.0.10", + "@angular/language-service": "~10.0.10", "@types/jasmine": "~3.5.0", "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/appsettings.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/appsettings.json index d45a2093e7..17ce15c462 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/appsettings.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/appsettings.json @@ -11,7 +11,8 @@ }, "MyProjectName_App": { "ClientId": "MyProjectName_App", - "ClientSecret": "1q2w3e*" + "ClientSecret": "1q2w3e*", + "RootUrl": "http://localhost:4200" } } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameDomainSharedModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameDomainSharedModule.cs index 885d98a7c2..b59dab0922 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameDomainSharedModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameDomainSharedModule.cs @@ -29,7 +29,8 @@ namespace MyCompanyName.MyProjectName { public override void PreConfigureServices(ServiceConfigurationContext context) { - MyProjectNameModulePropertyConfigurator.Configure(); + MyProjectNameGlobalFeatureConfigurator.Configure(); + MyProjectNameModuleExtensionConfigurator.Configure(); } public override void ConfigureServices(ServiceConfigurationContext context) @@ -48,7 +49,7 @@ namespace MyCompanyName.MyProjectName options.DefaultResourceType = typeof(MyProjectNameResource); }); - + Configure(options => { options.MapCodeNamespace("MyProjectName", typeof(MyProjectNameResource)); diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameGlobalFeatureConfigurator.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameGlobalFeatureConfigurator.cs new file mode 100644 index 0000000000..1b9b9731ab --- /dev/null +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameGlobalFeatureConfigurator.cs @@ -0,0 +1,23 @@ +using Volo.Abp.Threading; + +namespace MyCompanyName.MyProjectName +{ + public static class MyProjectNameGlobalFeatureConfigurator + { + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + /* You can configure (enable/disable) global features of the used modules here. + * + * YOU CAN SAFELY DELETE THIS CLASS AND REMOVE ITS USAGES IF YOU DON'T NEED TO IT! + * + * Please refer to the documentation to lear more about the Global Features System: + * https://docs.abp.io/en/commercial/latest/Global-Features + */ + }); + } + } +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameModuleExtensionConfigurator.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameModuleExtensionConfigurator.cs index ce3de2992f..61795eb4e5 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameModuleExtensionConfigurator.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyProjectNameModuleExtensionConfigurator.cs @@ -1,11 +1,11 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using Volo.Abp.Identity; using Volo.Abp.ObjectExtending; using Volo.Abp.Threading; namespace MyCompanyName.MyProjectName { - public static class MyProjectNameModulePropertyConfigurator + public static class MyProjectNameModuleExtensionConfigurator { private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); @@ -14,6 +14,7 @@ namespace MyCompanyName.MyProjectName OneTimeRunner.Run(() => { ConfigureExistingProperties(); + ConfigureExtraProperties(); }); } @@ -23,13 +24,49 @@ namespace MyCompanyName.MyProjectName * entities defined in the modules used by your application. * * Example: Change user and role name max lengths - + IdentityUserConsts.MaxNameLength = 99; IdentityRoleConsts.MaxNameLength = 99; - + * Notice: It is not suggested to change property lengths * unless you really need it. Go with the standard values wherever possible. + * + * If you are using EF Core, you will need to run the add-migration command after your changes. + */ + } + + private static void ConfigureExtraProperties() + { + /* You can configure extra properties for the + * entities defined in the modules used by your application. + * + * This class can be used to define these extra properties + * with a high level, easy to use API. + * + * Example: Add a new property to the user entity of the identity module + + ObjectExtensionManager.Instance.Modules() + .ConfigureIdentity(identity => + { + identity.ConfigureUser(user => + { + user.AddOrUpdateProperty( //property type: string + "SocialSecurityNumber", //property name + property => + { + //validation rules + property.Attributes.Add(new RequiredAttribute()); + property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4}); + + //...other configurations for this property + } + ); + }); + }); + + * See the documentation for more: + * https://docs.abp.io/en/latest/Module-Entity-Extensions */ } } -} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/IdentityServer/IdentityServerDataSeedContributor.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/IdentityServer/IdentityServerDataSeedContributor.cs index 2bce06c77d..805bc0e424 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/IdentityServer/IdentityServerDataSeedContributor.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/IdentityServer/IdentityServerDataSeedContributor.cs @@ -126,15 +126,20 @@ namespace MyCompanyName.MyProjectName.IdentityServer ); } - //Console Test Client - var consoleClientId = configurationSection["MyProjectName_App:ClientId"]; - if (!consoleClientId.IsNullOrWhiteSpace()) + //Console Test / Angular Client + var consoleAndAngularClientId = configurationSection["MyProjectName_App:ClientId"]; + if (!consoleAndAngularClientId.IsNullOrWhiteSpace()) { + var webClientRootUrl = configurationSection["MyProjectName_App:RootUrl"]?.TrimEnd('/'); + await CreateClientAsync( - name: consoleClientId, + name: consoleAndAngularClientId, scopes: commonScopes, - grantTypes: new[] {"password", "client_credentials"}, - secret: (configurationSection["MyProjectName_App:ClientSecret"] ?? "1q2w3e*").Sha256() + grantTypes: new[] {"password", "client_credentials", "authorization_code"}, + secret: (configurationSection["MyProjectName_App:ClientSecret"] ?? "1q2w3e*").Sha256(), + requireClientSecret: false, + redirectUri: webClientRootUrl, + postLogoutRedirectUri: webClientRootUrl ); } } @@ -147,6 +152,8 @@ namespace MyCompanyName.MyProjectName.IdentityServer string redirectUri = null, string postLogoutRedirectUri = null, string frontChannelLogoutUri = null, + bool requireClientSecret = true, + bool requirePkce = false, IEnumerable permissions = null) { var client = await _clientRepository.FindByCliendIdAsync(name); @@ -168,7 +175,9 @@ namespace MyCompanyName.MyProjectName.IdentityServer AuthorizationCodeLifetime = 300, IdentityTokenLifetime = 300, RequireConsent = false, - FrontChannelLogoutUri = frontChannelLogoutUri + FrontChannelLogoutUri = frontChannelLogoutUri, + RequireClientSecret = requireClientSecret, + RequirePkce = requirePkce }, autoSave: true ); diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs index 014594801f..91b05c877d 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDomainModule.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using MyCompanyName.MyProjectName.MultiTenancy; -using MyCompanyName.MyProjectName.ObjectExtending; using Volo.Abp.AuditLogging; using Volo.Abp.BackgroundJobs; using Volo.Abp.Emailing; @@ -32,11 +31,6 @@ namespace MyCompanyName.MyProjectName )] public class MyProjectNameDomainModule : AbpModule { - public override void PreConfigureServices(ServiceConfigurationContext context) - { - MyProjectNameDomainObjectExtensions.Configure(); - } - public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/ObjectExtending/MyProjectNameDomainObjectExtensions.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/ObjectExtending/MyProjectNameDomainObjectExtensions.cs deleted file mode 100644 index 6c1439e7c2..0000000000 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/ObjectExtending/MyProjectNameDomainObjectExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Volo.Abp.Identity; -using Volo.Abp.ObjectExtending; -using Volo.Abp.Threading; - -namespace MyCompanyName.MyProjectName.ObjectExtending -{ - public static class MyProjectNameDomainObjectExtensions - { - private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); - - public static void Configure() - { - OneTimeRunner.Run(() => - { - /* You can configure extension properties to entities or other object types - * defined in the depended modules. - * - * If you are using EF Core and want to map the entity extension properties to new - * table fields in the database, then configure them in the MyProjectNameEfCoreEntityExtensionMappings - * - * Example: - * - * ObjectExtensionManager.Instance - * .AddOrUpdateProperty("Title"); - * - * See the documentation for more: - * https://docs.abp.io/en/abp/latest/Object-Extensions - */ - }); - } - } -} \ No newline at end of file diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameEfCoreEntityExtensionMappings.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameEfCoreEntityExtensionMappings.cs index 0b19050cb0..eb0983c595 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameEfCoreEntityExtensionMappings.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/EntityFrameworkCore/MyProjectNameEfCoreEntityExtensionMappings.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Identity; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Identity; using Volo.Abp.ObjectExtending; using Volo.Abp.Threading; @@ -10,26 +11,31 @@ namespace MyCompanyName.MyProjectName.EntityFrameworkCore public static void Configure() { - MyProjectNameModulePropertyConfigurator.Configure(); - + MyProjectNameGlobalFeatureConfigurator.Configure(); + MyProjectNameModuleExtensionConfigurator.Configure(); + OneTimeRunner.Run(() => { - /* You can configure entity extension properties for the - * entities defined in the used modules. - * - * The properties defined here becomes table fields. - * If you want to use the ExtraProperties dictionary of the entity - * instead of creating a new field, then define the property in the - * MyProjectNameDomainObjectExtensions class. + /* You can configure extra properties for the + * entities defined in the modules used by your application. * - * Example: + * This class can be used to map these extra properties to table fields in the database. * - * ObjectExtensionManager.Instance - * .MapEfCoreProperty( - * "MyProperty", - * b => b.HasMaxLength(128) - * ); + * USE THIS CLASS ONLY TO CONFIGURE EF CORE RELATED MAPPING. + * USE MyProjectNameModuleExtensionConfigurator CLASS (in the Domain.Shared project) + * FOR A HIGH LEVEL API TO DEFINE EXTRA PROPERTIES TO ENTITIES OF THE USED MODULES * + * Example: Map a property to a table field: + + ObjectExtensionManager.Instance + .MapEfCoreProperty( + "MyProperty", + (entityBuilder, propertyBuilder) => + { + propertyBuilder.HasMaxLength(128); + } + ); + * See the documentation for more: * https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities */ diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj index 2776d2ed85..71b248c32e 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj @@ -13,7 +13,7 @@ - + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs index 7e7817ae87..3771a1874d 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs @@ -167,7 +167,10 @@ namespace MyCompanyName.MyProjectName { app.UseDeveloperExceptionPage(); } - else + + app.UseAbpRequestLocalization(); + + if (!env.IsDevelopment()) { app.UseErrorPage(); } @@ -183,7 +186,6 @@ namespace MyCompanyName.MyProjectName app.UseMultiTenancy(); } - app.UseAbpRequestLocalization(); app.UseAuthorization(); app.UseSwagger(); diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj index c7faed9653..373d19dcfb 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj @@ -13,7 +13,7 @@ - + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs index c671d0f223..95a26d9084 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs @@ -160,7 +160,10 @@ namespace MyCompanyName.MyProjectName { app.UseDeveloperExceptionPage(); } - else + + app.UseAbpRequestLocalization(); + + if (!env.IsDevelopment()) { app.UseErrorPage(); } @@ -177,7 +180,6 @@ namespace MyCompanyName.MyProjectName app.UseMultiTenancy(); } - app.UseAbpRequestLocalization(); app.UseIdentityServer(); app.UseAuthorization(); diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs index df28fe0ccd..8a4e919161 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs @@ -143,7 +143,10 @@ namespace MyCompanyName.MyProjectName { app.UseDeveloperExceptionPage(); } - else + + app.UseAbpRequestLocalization(); + + if (!env.IsDevelopment()) { app.UseErrorPage(); } @@ -159,7 +162,6 @@ namespace MyCompanyName.MyProjectName app.UseMultiTenancy(); } - app.UseAbpRequestLocalization(); app.UseIdentityServer(); app.UseAuthorization(); app.UseAuditing(); diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/Pages/Index.cshtml b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/Pages/Index.cshtml index 5bc466601f..db9f1f100a 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/Pages/Index.cshtml +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/Pages/Index.cshtml @@ -9,7 +9,7 @@ -
Logout + Logout

@CurrentUser.UserName

@@ -31,4 +31,4 @@

Login
-} \ No newline at end of file +} diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj index 469d83804c..777288a513 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj @@ -19,7 +19,7 @@ - + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs index 689fe33eca..699db2cec0 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs @@ -215,7 +215,10 @@ namespace MyCompanyName.MyProjectName.Web { app.UseDeveloperExceptionPage(); } - else + + app.UseAbpRequestLocalization(); + + if (!env.IsDevelopment()) { app.UseErrorPage(); } @@ -230,7 +233,6 @@ namespace MyCompanyName.MyProjectName.Web app.UseMultiTenancy(); } - app.UseAbpRequestLocalization(); app.UseAuthorization(); app.UseSwagger(); diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenuContributor.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenuContributor.cs index 9e1f3a835c..e772db8972 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenuContributor.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenuContributor.cs @@ -28,7 +28,7 @@ namespace MyCompanyName.MyProjectName.Web.Menus var l = context.GetLocalizer(); - context.Menu.Items.Insert(0, new ApplicationMenuItem("MyProjectName.Home", l["Menu:Home"], "~/")); + context.Menu.Items.Insert(0, new ApplicationMenuItem(MyProjectNameMenus.Home, l["Menu:Home"], "~/")); } } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenus.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenus.cs new file mode 100644 index 0000000000..d4563554b4 --- /dev/null +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenus.cs @@ -0,0 +1,11 @@ +namespace MyCompanyName.MyProjectName.Web.Menus +{ + public class MyProjectNameMenus + { + private const string Prefix = "MyProjectName"; + public const string Home = Prefix + ".Home"; + + //Add your menu items here... + + } +} \ No newline at end of file diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj index de399f2ef5..eb374cb80e 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj @@ -35,7 +35,7 @@ - + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs index 1bafd38d5a..cf201f90cc 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs @@ -191,7 +191,10 @@ namespace MyCompanyName.MyProjectName.Web { app.UseDeveloperExceptionPage(); } - else + + app.UseAbpRequestLocalization(); + + if (!env.IsDevelopment()) { app.UseErrorPage(); } @@ -207,7 +210,6 @@ namespace MyCompanyName.MyProjectName.Web app.UseMultiTenancy(); } - app.UseAbpRequestLocalization(); app.UseIdentityServer(); app.UseAuthorization(); app.UseSwagger(); diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyProjectNameWebTestModule.cs b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyProjectNameWebTestModule.cs index 28b5e3b55b..8f7b9e2daa 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyProjectNameWebTestModule.cs +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyProjectNameWebTestModule.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Globalization; using Localization.Resources.AbpUi; @@ -6,11 +5,9 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using MyCompanyName.MyProjectName.Localization; using MyCompanyName.MyProjectName.Web; using MyCompanyName.MyProjectName.Web.Menus; -using Volo.Abp; using Volo.Abp.AspNetCore.TestBase; using Volo.Abp.Localization; using Volo.Abp.Modularity; @@ -68,46 +65,5 @@ namespace MyCompanyName.MyProjectName options.MenuContributors.Add(new MyProjectNameMenuContributor()); }); } - - public override void OnApplicationInitialization(ApplicationInitializationContext context) - { - var app = context.GetApplicationBuilder(); - var env = context.GetEnvironment(); - - app.Use(async (ctx, next) => - { - try - { - await next(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - }); - - app.UseVirtualFiles(); - app.UseRouting(); - app.UseAuthentication(); - app.UseAbpRequestLocalization(); - app.UseAuthorization(); - - - app.Use(async (ctx, next) => - { - try - { - await next(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - }); - - app.UseConfiguredEndpoints(); - } } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj index 589ec5df65..35e73d4868 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj index 17f9cdb076..c70808deed 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj @@ -10,7 +10,7 @@ - + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj index 3923a71691..f9e00d53d3 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj index 3cd14d2086..8f278d0a03 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj @@ -12,7 +12,7 @@ - + diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj index 90baabf104..1817e26039 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj @@ -27,9 +27,13 @@ + + + +