diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index 0caa284764..34d20a3bda 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -421,7 +421,7 @@ "ContentCacheSlidingExpirationByDay": "Content Cache Sliding Expiration By Day", "MaxDaysForCaching": "Max Days For Caching", "Enabled": "Enabled", - "Menu:NugetPackagesContentCache": "NuGet Packages Content Cache", + "Menu:NugetPackagesContentCache": "NuGet Cache", "NugetPackagesContentCache": "NuGet Content Cache", "SlidingExpritionByDayInfo": "Gets or sets how long a cache entry can be inactive (e.g. not accessed) before it will be removed. This will not extend the entry lifetime beyond the absolute expiration.", "MaxDaysForCachingInfo": "Gets or sets an absolute expiration time, relative to now.", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json index 89f7648368..0b097d53a8 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json @@ -27,6 +27,7 @@ "Volo.AbpIo.Domain:030010": "To purchase the trial license, you first need to activate your trial license!", "Volo.AbpIo.Domain:030011": "You cannot delete a trial license when it is purchased!", "Volo.AbpIo.Domain:030012": "A user is entitled to have only 1 free trial period. You already used your trial license.", + "Volo.AbpIo.Domain:030013": "A user with an active license cannot start a trial license.", "Volo.AbpIo.Domain:070000": "The organization name can only contain latin letters, numbers, dots and hyphens!", "Volo.AbpIo.Domain:070001": "The company name can only contain latin letters, numbers, dots, space and hyphens!", "WantToLearn?": "Want to learn?", @@ -205,6 +206,8 @@ "Icons": "Icons", "Url": "Url", "Icon": "Icon", - "RecentActivities": "Recent Activities" + "RecentActivities": "Recent Activities", + "SpringCampaign": "Welcome
Spring Sale!", + "SpringCampaign2": "Limited
Time Offer!
" } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json index 6e5e69f079..d06e505e40 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json @@ -196,7 +196,7 @@ "ChangingDevelopers": "Can I change the registered developers of my organization in the future?", "ChangingDevelopersExplanation": "In addition to adding new developers to your license, you can also change the existing developers (you can remove a developer and add a new one to the same seat) without any additional cost.", "WhatHappensWhenLicenseEnds": "What happens when my license period ends?", - "WhatHappensWhenLicenseEndsExplanation1": "The ABP Commercial license is a perpetual license. After your license expires, you can continue developing your project. And you are not obliged to renew your license. Your license comes with a one-year update and support plan out of the box. In order to continue to get new features, performance enhancements, bug fixes, support and continue using ABP Suite, you need to renew your license. When your license expires, you will not get the following benefits:", + "WhatHappensWhenLicenseEndsExplanation1": "The ABP Commercial license is a perpetual license. After your license expires, you can continue developing your project. And you are not obliged to renew your license. Your license comes with a one-year update and support plan out of the box. In order to continue to get new features, performance enhancements, bug fixes, support and continue using ABP Suite, you need to renew your license. When your license expires;", "WhatHappensWhenLicenseEndsExplanation2": "You can not create new solutions using the ABP Commercial, but you can continue developing your existing applications forever.", "WhatHappensWhenLicenseEndsExplanation3": "You will be able to get updates for the modules and themes within your MINOR version (except RC or Preview versions). For example: if you are using v3.2.0 of a module, you can still get updates for v3.2.x (v3.2.1, v3.2.5... etc.) of that module. But you cannot get updates for the next major or minor version (like v3.3.0, v3.3.3, 4.x.x.. etc.). For example, when your license expired, the latest release was v4.4.3, and later, it published both 4.4.4 version and 4.5.0 version, you would be able to access the v4.4.X but you wouldn't be access the v4.5.X.", "WhatHappensWhenLicenseEndsExplanation4": "You can not install new modules and themes added to the ABP Commercial platform after your license ends.", @@ -208,7 +208,7 @@ "WhenShouldIRenewMyLicense": "When should I renew my license?", "WhenShouldIRenewMyLicenseExplanation": "If you renew your license within 1 month after your license expires, the following discounts will be applied: Team License {0}; Business License {1}; Enterprise License {2}. However, if you renew your license after 1 month since the expiry date of your license, the renewal price will be the same as the license purchase price and there will be no discount on your renewal.", "TrialPlan": "Do you have a trial plan?", - "TrialPlanExplanation": "It has a 14 days trial period for the ABP Commercial team license. For more information visit here. Furthermore, for the Team licenses we provide a 30 days money-back guarantee. You can just request a refund in the first 30 days. For the Business and Enterprise licenses, we provide 60% refund in 30 days. This is because Business and Enterprise licenses include the full source code of all the modules and the themes.", + "TrialPlanExplanation": "No, there is no trial version for ABP Commercial. You can check the community edition to understand the code quality and approaches. We also offer a 30-day money-back guarantee for the Team license, no questions asked! You can request a refund within the first 30 days. We provide a 60% refund within 30 days for Business and Enterprise licenses. This is because the Business and Enterprise licenses contain the full source-code of all the modules and themes.", "DoYouAcceptBankWireTransfer": "Do you accept bank wire transfers?", "DoYouAcceptBankWireTransferExplanation": "Yes, we accept bank wire transfers.
After sending the license fee via bank transfer, send your receipt and requested license type to accounting@volosoft.com.
Our international bank account information:", "HowToUpgrade": "How to upgrade existing applications when a new version is available?", @@ -818,6 +818,7 @@ "DeletingMemberWarningMessage": "\"{0}\" will be removed from the developer list. If you want, you can assign this empty seat to another developer later.", "AdditionalInfo": "If the developer seats are above your requirements, you can reduce them. You can email at info@abp.io to remove some of your developer seats. Clearing unused developer seats will reduce the license renewal cost. If you want, you can re-purchase additional developer seats within your active license period. Note that, since there are {0} developers in this license package, you cannot reduce this number.", "LinkExpiredErrorMessage": "The link you are trying to access is expired.", - "ExpirationDate": "Expiration Date" + "ExpirationDate": "Expiration Date", + "SpringCampaignDiscount": "Spring Campaign Discount" } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json index 8cfb154295..953625f9cd 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json @@ -184,6 +184,8 @@ "Layout_MetaDescription": "ABP Community is an environment where people can share posts about ABP framework and follows the projects.", "Index_Page_CommunityIntroduction": "This is a hub for ABP Framework, .NET and software development. You can read the articles, watch the video tutorials, get informed about ABP’s development progress and ABP-related events, help other developers and share your expertise with the ABP community.", "TagsInArticle": "Tags in article", - "IConsentToMedium": "I consent to the publication of this post at https://medium.com/volosoft." + "IConsentToMedium": "I consent to the publication of this post at https://medium.com/volosoft.", + "SearchResultsFor": "Search results for \"{0}\"", + "SeeMoreVideos": "See more videos" } } diff --git a/common.props b/common.props index b03f345bf5..262ed2cf6a 100644 --- a/common.props +++ b/common.props @@ -1,7 +1,7 @@ latest - 7.1.0-rc.3 + 7.2.0 $(NoWarn);CS1591;CS0436 https://abp.io/assets/abp_nupkg.png https://abp.io/ diff --git a/docs/en/Background-Jobs.md b/docs/en/Background-Jobs.md index 84e746f73d..c6e19fb48a 100644 --- a/docs/en/Background-Jobs.md +++ b/docs/en/Background-Jobs.md @@ -75,6 +75,42 @@ This job simply uses `IEmailSender` to send emails (see [email sending document] A background job should not hide exceptions. If it throws an exception, the background job is automatically re-tried after a calculated waiting time. Hide exceptions only if you don't want to re-run the background job for the current argument. +#### Cancelling Background Jobs + +If your background task is cancellable, then you can use the standard [Cancellation Token](Cancellation-Token-Provider.md) system to obtain a `CancellationToken` to cancel your job when requested. See the following example that uses the `ICancellationTokenProvider` to obtain the cancellation token: + +```csharp +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; + +namespace MyProject +{ + public class LongRunningJob : AsyncBackgroundJob, ITransientDependency + { + private readonly ICancellationTokenProvider _cancellationTokenProvider; + + public LongRunningJob(ICancellationTokenProvider cancellationTokenProvider) + { + _cancellationTokenProvider = cancellationTokenProvider; + } + + public override async Task ExecuteAsync(LongRunningJobArgs args) + { + foreach (var id in args.Ids) + { + _cancellationTokenProvider.Token.ThrowIfCancellationRequested(); + await ProcessAsync(id); // code omitted for brevity + } + } + } +} +``` + +> A cancellation operation might be needed if the application is shutting down and we don't want to block the application in the background job. This example throws an exception if the cancellation is requested. So, the job will be retried the next time the application starts. If you don't want that, just return from the `ExecuteAsync` method without throwing any exception (you can simply check the `_cancellationTokenProvider.Token.IsCancellationRequested` property). + #### Job Name Each background job has a name. Job names are used in several places. For example, RabbitMQ provider uses job names to determine the RabbitMQ Queue names. diff --git a/docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/POST.md b/docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/POST.md new file mode 100644 index 0000000000..63936521c6 --- /dev/null +++ b/docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/POST.md @@ -0,0 +1,44 @@ +## Streamline Localization in Your ABP Project + +Making localization changes to an ABP project can be a daunting task, especially if you're dealing with multiple languages and translations. During development, it's easy to overlook some changes and that leads to inconsistencies across different languages. Fortunately, I have developed a tool that can help streamline the localization process and ensure consistency across different languages. + +The tool is a console application that uses JSON files to manage localization keys and their translations. It addresses three common scenarios that can arise during localization: + + +1. When the argument count of a key changes, it can be difficult to update the translations for all languages. My tool solves this problem by scanning all JSON files in the project folder and identifying any keys that have mismatched argument counts. It then offers two options to the user: delete the mismatched translations or export them as a JSON file for manual editing. + +2. When a new key is added to the project, forgetting to add its translations to all the other languages is easy. My tool helps to avoid this issue by scanning the default language's JSON file and identifying any keys that don't have translations in other languages. It then exports these keys as a JSON file that can be used to add missing translations. + +3. When a key's name is changed, it's important to update its translations in all the other languages. My tool makes this task simple by scanning all the JSON files in the project folder and updating any translations of the old key name with the new one. + +The tool also includes an export feature that allows users to modify translations outside of the application and import them back into the JSON files. + +## How it Helps + +With my Localization Key Synchronizer tool, you can perform complex localization changes more quickly and easily than by manually sifting through files and making changes one-by-one. This can save you significant time and effort, especially if you're working with a large number of languages or translations. + +## How it Works + +When you run the Localization Key Synchronizer tool, it presents you with three options: + +1. Find Asynchronous Keys +2. Apply Changes in the Exported File +3. Replace Keys + +If you select "Find Asynchronous Keys," the tool prompts you to enter the default language path. Once you've entered the path, the tool displays all of the JSON files in the same folder as a multi-select list. After selecting one or more files, you are asked whether you want to find keys that do not match the number of arguments, missing keys, or both. If you select "Missing Keys," the tool prompts you to enter the absolute path to export the missing keys. After you've entered the path, the export process starts, and the tool closes. + +![](./images/Part1.gif) + +If you select "Apply Changes in the Exported File" at the main menu, the tool prompts you to enter the path to the exported file. After you've entered the path, the import process starts, and the tool closes. + +![](./images/Part2.gif) + +If you select "Replace Keys," the tool prompts you to enter the localization folder path, the old key, the new key, and the JSON files to apply the changes to. Once you've entered all the required information and made your selections, the tool performs the replacements and closes. + +![](./images/Part3.gif) + +## Conclusion + +If you're struggling to manage localization changes in an ABP project, give my Localization Key Synchronizer tool a try. It can help streamline your workflow and make the process much more manageable. You can find the tool on [GitHub](https://github.com/abpframework/abp/tree/dev/tools/localization-key-synchronizer). + +To use the tool, simply run the console application and follow the prompts. It's a user-friendly solution that helps to ensure localization consistency in your ABP project. Give it a try and let me know what you think! diff --git a/docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part1.gif b/docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part1.gif new file mode 100644 index 0000000000..f2e6b66e64 Binary files /dev/null and b/docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part1.gif differ diff --git a/docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part2.gif b/docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part2.gif new file mode 100644 index 0000000000..9bc70d361d Binary files /dev/null and b/docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part2.gif differ diff --git a/docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part3.gif b/docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part3.gif new file mode 100644 index 0000000000..8a8ae32d25 Binary files /dev/null and b/docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part3.gif differ diff --git a/docs/en/Dependency-Injection.md b/docs/en/Dependency-Injection.md index 47e60700bc..206c2c58a4 100644 --- a/docs/en/Dependency-Injection.md +++ b/docs/en/Dependency-Injection.md @@ -440,16 +440,16 @@ Use `ICachedServiceProvider` (instead of `ITransientCachedServiceProvider`) unle ## Advanced Features -### IServiceCollection.OnRegistred Event +### IServiceCollection.OnRegistered Event -You may want to perform an action for every service registered to the dependency injection. In the `PreConfigureServices` method of your module, register a callback using the `OnRegistred` method as shown below: +You may want to perform an action for every service registered to the dependency injection. In the `PreConfigureServices` method of your module, register a callback using the `OnRegistered` method as shown below: ````csharp public class AppModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services.OnRegistred(ctx => + context.Services.OnRegistered(ctx => { var type = ctx.ImplementationType; //... @@ -465,7 +465,7 @@ public class AppModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services.OnRegistred(ctx => + context.Services.OnRegistered(ctx => { if (ctx.ImplementationType.IsDefined(typeof(MyLogAttribute), true)) { @@ -478,7 +478,7 @@ public class AppModule : AbpModule This example simply checks if the service class has `MyLogAttribute` attribute and adds `MyLogInterceptor` to the interceptor list if so. -> Notice that `OnRegistred` callback might be called multiple times for the same service class if it exposes more than one service/interface. So, it's safe to use `Interceptors.TryAdd` method instead of `Interceptors.Add` method. See [the documentation](Dynamic-Proxying-Interceptors.md) of dynamic proxying / interceptors. +> Notice that `OnRegistered` callback might be called multiple times for the same service class if it exposes more than one service/interface. So, it's safe to use `Interceptors.TryAdd` method instead of `Interceptors.Add` method. See [the documentation](Dynamic-Proxying-Interceptors.md) of dynamic proxying / interceptors. ## 3rd-Party Providers diff --git a/docs/en/Multi-Tenancy.md b/docs/en/Multi-Tenancy.md index 7dac3762aa..19cf8fa443 100644 --- a/docs/en/Multi-Tenancy.md +++ b/docs/en/Multi-Tenancy.md @@ -32,25 +32,6 @@ Configure(options => > Multi-Tenancy is disabled in the ABP Framework by default. However, it is **enabled by default** when you create a new solution using the [startup template](Startup-Templates/Application.md). `MultiTenancyConsts` class in the solution has a constant to control it in a single place. -### AbpMultiTenancyOptions: Handle inactive and non-existent tenants. - -The `MultiTenancyMiddlewareErrorPageBuilder` of `AbpMultiTenancyOptions` is used to handle inactive and non-existent tenants. - -It will respond to an error page by default, you can change it if you want, eg: only output the error log and continue ASP NET Core's request pipeline. - -```csharp -Configure(options => -{ - options.MultiTenancyMiddlewareErrorPageBuilder = async (context, exception) => - { - // Handle the exception. - - // Return true to stop the pipeline, false to continue. - return true; - }; -}); -``` - ### Database Architecture ABP Framework supports all the following approaches to store the tenant data in the database; @@ -273,6 +254,23 @@ class SomeComponent { > However, we don't suggest to change this value since some clients may assume the the `__tenant` as the parameter name and they might need to manually configure then. +The `MultiTenancyMiddlewareErrorPageBuilder` is used to handle inactive and non-existent tenants. + +It will respond to an error page by default, you can change it if you want, eg: only output the error log and continue ASP NET Core's request pipeline. + +```csharp +Configure(options => +{ + options.MultiTenancyMiddlewareErrorPageBuilder = async (context, exception) => + { + // Handle the exception. + + // Return true to stop the pipeline, false to continue. + return true; + }; +}); +``` + ##### Domain/Subdomain Tenant Resolver In a real application, most of times you will want to determine current tenant either by subdomain (like mytenant1.mydomain.com) or by the whole domain (like mytenant.com). If so, you can configure the `AbpTenantResolveOptions` to add the domain tenant resolver. diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md index 6178ebbb8e..e71beccd6f 100644 --- a/docs/en/Tutorials/Part-10.md +++ b/docs/en/Tutorials/Part-10.md @@ -361,7 +361,7 @@ public class BookAppService : GetListPolicyName = BookStorePermissions.Books.Default; CreatePolicyName = BookStorePermissions.Books.Create; UpdatePolicyName = BookStorePermissions.Books.Edit; - DeletePolicyName = BookStorePermissions.Books.Create; + DeletePolicyName = BookStorePermissions.Books.Delete; } public override async Task GetAsync(Guid id) diff --git a/docs/en/UI/Angular/Card-Component.md b/docs/en/UI/Angular/Card-Component.md new file mode 100644 index 0000000000..5c92d7abed --- /dev/null +++ b/docs/en/UI/Angular/Card-Component.md @@ -0,0 +1,52 @@ +# Card Component + +The ABP Card Component is a wrapper component for the Bootstrap card class. + +## Usage + +ABP Card Component is a part of the `ThemeSharedModule` module. If you've imported that module into your module, you don't need to import it again. If not, first import it as shown below: + +```ts +// my-feature.module.ts + +import { ThemeSharedModule } from '@abp/ng.theme.shared'; +import { CardDemoComponent } from './chart-demo.component'; + +@NgModule({ + imports: [ + ThemeSharedModule , + // ... + ], + declarations: [CardDemoComponent], + // ... +}) +export class MyFeatureModule {} + +``` + +Then, the `abp-card` component can be used. See the example below: +```ts + +// card-demo.component.ts + +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-card-demo', + template: ` + + + Lorem Ipsum + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla commodo condimentum ligula, sed varius nibh eleifend sit amet. Maecenas facilisis vel arcu nec maximus. + + + `, +}) +export class CardDemoComponent { } +``` + +See the result below: + +![abp-card-component](./images/abp-card-component.png) + +As you can see in the example above, you can customize your card component's style with the `cardStyle` input. You can also add your custom classes with the `cardClass` input. diff --git a/docs/en/UI/Angular/Quick-Start.md b/docs/en/UI/Angular/Quick-Start.md index 768b3b7b67..dd26dd3efe 100644 --- a/docs/en/UI/Angular/Quick-Start.md +++ b/docs/en/UI/Angular/Quick-Start.md @@ -179,7 +179,7 @@ This command will download and start a simple static server, a browser window at Of course, you need your application to run on an optimized web server and become available to everyone. This is quite straight-forward: -1. Create a new static web server instance. You can use a service like [Azure App Service](https://azure.microsoft.com/tr-tr/services/app-service/web/), [Firebase](https://firebase.google.com/docs/hosting), [Netlify](https://www.netlify.com/), [Vercel](https://vercel.com/), or even [GitHub Pages](https://angular.io/guide/deployment#deploy-to-github-pages). Another option is maintaining own web server with [NGINX](https://www.nginx.com/), [IIS](https://www.iis.net/), [Apache HTTP Server](https://httpd.apache.org/), or equivalent. +1. Create a new static web server instance. You can use a service like [Azure App Service](https://azure.microsoft.com/en-us/services/app-service/web/), [Firebase](https://firebase.google.com/docs/hosting), [Netlify](https://www.netlify.com/), [Vercel](https://vercel.com/), or even [GitHub Pages](https://angular.io/guide/deployment#deploy-to-github-pages). Another option is maintaining own web server with [NGINX](https://www.nginx.com/), [IIS](https://www.iis.net/), [Apache HTTP Server](https://httpd.apache.org/), or equivalent. 2. Copy the files from `dist/MyProjectName` [1](#f-dist-folder-name) to a publicly served destination on the server via CLI of the service provider, SSH, or FTP (whichever is available). This step would be defined as a job if you have a CI/CD flow. 3. [Configure the server](https://angular.io/guide/deployment#server-configuration) to redirect all requests to the _index.html_ file. Some services do that automatically. Others require you [to add a file to the bundle via assets](https://angular.io/guide/workspace-config#assets-configuration) which describes the server how to do the redirections. Occasionally, you may need to do manual configuration. diff --git a/docs/en/UI/Angular/images/abp-card-component.png b/docs/en/UI/Angular/images/abp-card-component.png new file mode 100644 index 0000000000..726c617772 Binary files /dev/null and b/docs/en/UI/Angular/images/abp-card-component.png differ diff --git a/docs/en/UI/AspNetCore/Navigation-Menu.md b/docs/en/UI/AspNetCore/Navigation-Menu.md index f0776f47ec..60621f9906 100644 --- a/docs/en/UI/AspNetCore/Navigation-Menu.md +++ b/docs/en/UI/AspNetCore/Navigation-Menu.md @@ -104,6 +104,7 @@ There are more options of a menu item (the constructor of the `ApplicationMenuIt * `target` (`string`): Target of the menu item. Can be `null` (default), "\_*blank*", "\_*self*", "\_*parent*", "\_*top*" or a frame name for web applications. * `elementId` (`string`): Can be used to render the element with a specific HTML `id` attribute. * `cssClass` (`string`): Additional string classes for the menu item. +* `groupName` (`string`): Can be used to group menu items. ### Authorization @@ -179,6 +180,58 @@ userMenu.Icon = "fa fa-users"; > `context.Menu` gives you ability to access to all the menu items those have been added by the previous menu contributors. +### Menu Groups + +You can define groups and associate menu items with a group. + +Example: + +```csharp +using System.Threading.Tasks; +using MyProject.Localization; +using Volo.Abp.UI.Navigation; + +namespace MyProject.Web.Menus +{ + public class MyProjectMenuContributor : IMenuContributor + { + public async Task ConfigureMenuAsync(MenuConfigurationContext context) + { + if (context.Menu.Name == StandardMenus.Main) + { + await ConfigureMainMenuAsync(context); + } + } + + private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) + { + var l = context.GetLocalizer(); + + context.Menu.AddGroup( + new ApplicationMenuGroup( + name: "Main", + displayName: l["Main"] + ) + ) + context.Menu.AddItem( + new ApplicationMenuItem("MyProject.Crm", l["Menu:CRM"], groupName: "Main") + .AddItem(new ApplicationMenuItem( + name: "MyProject.Crm.Customers", + displayName: l["Menu:Customers"], + url: "/crm/customers") + ).AddItem(new ApplicationMenuItem( + name: "MyProject.Crm.Orders", + displayName: l["Menu:Orders"], + url: "/crm/orders") + ) + ); + } + } +} +``` + +> The UI theme will decide whether to render the groups or not, and if it decides to render, the way it's rendered is up to the theme. Only the LeptonX theme implements the menu group. + ## Standard Menus A menu is a **named** component. An application may contain more than one menus with different, unique names. There are two pre-defined standard menus: @@ -233,4 +286,3 @@ namespace MyProject.Web.Pages } } ``` - diff --git a/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md b/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md index 8f664a80a5..15a8b0819c 100644 --- a/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md +++ b/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md @@ -10,7 +10,7 @@ See the [form elements demo page](https://bootstrap-taghelpers.abp.io/Components ## abp-input -`abp-input` tag creates a Bootstrap form input for a given c# property. It uses [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/tr-tr/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-input-tag-helper) in background, so every data annotation attribute of `input` tag helper of Asp.Net Core is also valid for `abp-input`. +`abp-input` tag creates a Bootstrap form input for a given c# property. It uses [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-input-tag-helper) in background, so every data annotation attribute of `input` tag helper of Asp.Net Core is also valid for `abp-input`. Usage: @@ -88,7 +88,7 @@ You can set some of the attributes on your c# property, or directly on html tag. * `label`: Sets the label for input. * `display-required-symbol`: Adds the required symbol (*) to label if input is required. Default `True`. -`asp-format`, `name` and `value` attributes of [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-input-tag-helper) are also valid for `abp-input` tag helper. +`asp-format`, `name` and `value` attributes of [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-input-tag-helper) are also valid for `abp-input` tag helper. ### Label & Localization @@ -100,7 +100,7 @@ You can set label of your input in different ways: ## abp-select -`abp-select` tag creates a Bootstrap form select for a given c# property. It uses [Asp.Net Core Select Tag Helper](https://docs.microsoft.com/tr-tr/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-select-tag-helper) in background, so every data annotation attribute of `select` tag helper of Asp.Net Core is also valid for `abp-select`. +`abp-select` tag creates a Bootstrap form select for a given c# property. It uses [Asp.Net Core Select Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-7.0#the-select-tag-helper) in background, so every data annotation attribute of `select` tag helper of Asp.Net Core is also valid for `abp-select`. `abp-select` tag needs a list of `Microsoft.AspNetCore.Mvc.Rendering.SelectListItem ` to work. It can be provided by `asp-items` attriube on the tag or `[SelectItems()]` attribute on c# property. (if you are using [abp-dynamic-form](Dynamic-forms.md), c# attribute is the only way.) diff --git a/docs/en/UI/Blazor/Basic-Theme.md b/docs/en/UI/Blazor/Basic-Theme.md index 4060f097ba..e4124d9a2c 100644 --- a/docs/en/UI/Blazor/Basic-Theme.md +++ b/docs/en/UI/Blazor/Basic-Theme.md @@ -85,6 +85,27 @@ You can simply override the styles in the Global Styles file of your application See the [Customization / Overriding Components](Customization-Overriding-Components.md) to learn how you can replace components, customize and extend the user interface. +### Overriding the Menu Item +Basic theme supports overriding a single menu item with a custom component. You can create a custom component and call `UseComponent` extension method of Basic Theme in the **MenuContributor**. + +```csharp +using Volo.Abp.AspNetCore.Components.Web.BasicTheme.Navigation; + +//... + +context.Menu.Items.Add( + new ApplicationMenuItem("Custom.1", "My Custom Menu", "#") + .UseComponent(typeof(MyMenuItemComponent))); +``` + +```html + +``` + ### Copy & Customize You can run the following [ABP CLI](../../CLI.md) command in **Blazor{{if UI == "Blazor"}}WebAssembly{{else}} Server{{end}}** project directory to copy the source code to your solution: diff --git a/docs/en/UI/Blazor/Navigation-Menu.md b/docs/en/UI/Blazor/Navigation-Menu.md index 7f09a65dbb..0abac5fc68 100644 --- a/docs/en/UI/Blazor/Navigation-Menu.md +++ b/docs/en/UI/Blazor/Navigation-Menu.md @@ -104,6 +104,7 @@ There are more options of a menu item (the constructor of the `ApplicationMenuIt * `target` (`string`): Target of the menu item. Can be `null` (default), "\_*blank*", "\_*self*", "\_*parent*", "\_*top*" or a frame name for web applications. * `elementId` (`string`): Can be used to render the element with a specific HTML `id` attribute. * `cssClass` (`string`): Additional string classes for the menu item. +* `groupName` (`string`): Can be used to group menu items. ### Authorization @@ -160,6 +161,58 @@ userMenu.Icon = "fa fa-users"; > `context.Menu` gives you ability to access to all the menu items those have been added by the previous menu contributors. +### Menu Groups + +You can define groups and associate menu items with a group. + +Example: + +```csharp +using System.Threading.Tasks; +using MyProject.Localization; +using Volo.Abp.UI.Navigation; + +namespace MyProject.Web.Menus +{ + public class MyProjectMenuContributor : IMenuContributor + { + public async Task ConfigureMenuAsync(MenuConfigurationContext context) + { + if (context.Menu.Name == StandardMenus.Main) + { + await ConfigureMainMenuAsync(context); + } + } + + private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) + { + var l = context.GetLocalizer(); + + context.Menu.AddGroup( + new ApplicationMenuGroup( + name: "Main", + displayName: l["Main"] + ) + ) + context.Menu.AddItem( + new ApplicationMenuItem("MyProject.Crm", l["Menu:CRM"], groupName: "Main") + .AddItem(new ApplicationMenuItem( + name: "MyProject.Crm.Customers", + displayName: l["Menu:Customers"], + url: "/crm/customers") + ).AddItem(new ApplicationMenuItem( + name: "MyProject.Crm.Orders", + displayName: l["Menu:Orders"], + url: "/crm/orders") + ) + ); + } + } +} +``` + +> The UI theme will decide whether to render the groups or not, and if it decides to render, the way it's rendered is up to the theme. Only the LeptonX theme implements the menu group. + ## Standard Menus A menu is a **named** component. An application may contain more than one menus with different, unique names. There are two pre-defined standard menus: diff --git a/docs/en/UI/Blazor/Overall.md b/docs/en/UI/Blazor/Overall.md index 835bf4eab6..f38c0c257b 100644 --- a/docs/en/UI/Blazor/Overall.md +++ b/docs/en/UI/Blazor/Overall.md @@ -88,7 +88,7 @@ There are a set of standard libraries that comes pre-installed and supported by * [Twitter Bootstrap](https://getbootstrap.com/) as the fundamental HTML/CSS framework. * [Blazorise](https://github.com/stsrki/Blazorise) as a component library that supports the Bootstrap and adds extra components like Data Grid and Tree. * [FontAwesome](https://fontawesome.com/) as the fundamental CSS font library. -* [Flag Icon](https://github.com/lipis/flag-icon-css) as a library to show flags of countries. +* [Flag Icon](https://github.com/lipis/flag-icons) as a library to show flags of countries. These libraries are selected as the base libraries and available to the applications and modules. diff --git a/docs/en/UI/Blazor/Theming.md b/docs/en/UI/Blazor/Theming.md index d8cec07cb3..2496df9b20 100644 --- a/docs/en/UI/Blazor/Theming.md +++ b/docs/en/UI/Blazor/Theming.md @@ -48,7 +48,7 @@ All the themes must depend on the [Volo.Abp.AspNetCore.Components.Server.Theming * [Twitter Bootstrap](https://getbootstrap.com/) as the fundamental HTML/CSS framework. * [Blazorise](https://github.com/stsrki/Blazorise) as a component library that supports the Bootstrap and adds extra components like Data Grid and Tree. * [FontAwesome](https://fontawesome.com/) as the fundamental CSS font library. -* [Flag Icon](https://github.com/lipis/flag-icon-css) as a library to show flags of countries. +* [Flag Icon](https://github.com/lipis/flag-icons) as a library to show flags of countries. These libraries are selected as the base libraries and available to the applications and modules. diff --git a/docs/zh-Hans/Dependency-Injection.md b/docs/zh-Hans/Dependency-Injection.md index 2264a12930..2c22c123ab 100644 --- a/docs/zh-Hans/Dependency-Injection.md +++ b/docs/zh-Hans/Dependency-Injection.md @@ -270,16 +270,16 @@ using (var scope = _serviceProvider.CreateScope()) ## 高级特性 -### IServiceCollection.OnRegistred 事件 +### IServiceCollection.OnRegistered 事件 -你可能想在注册到依赖注入的每个服务上执行一个操作, 在你的模块的 `PreConfigureServices` 方法中, 使用 `OnRegistred` 方法注册一个回调(callback) , 如下所示: +你可能想在注册到依赖注入的每个服务上执行一个操作, 在你的模块的 `PreConfigureServices` 方法中, 使用 `OnRegistered` 方法注册一个回调(callback) , 如下所示: ````csharp public class AppModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services.OnRegistred(ctx => + context.Services.OnRegistered(ctx => { var type = ctx.ImplementationType; //... @@ -295,7 +295,7 @@ public class AppModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services.OnRegistred(ctx => + context.Services.OnRegistered(ctx => { if (ctx.ImplementationType.IsDefined(typeof(MyLogAttribute), true)) { @@ -308,7 +308,7 @@ public class AppModule : AbpModule 这个示例判断一个服务类是否具有 `MyLogAttribute` 特性, 如果有的话就添加一个 `MyLogInterceptor` 到拦截器集合中. -> 注意, 如果服务类公开了多于一个服务或接口, `OnRegistred` 回调(callback)可能被同一服务类多次调用. 因此, 较安全的方法是使用 `Interceptors.TryAdd` 方法而不是 `Interceptors.Add` 方法. 请参阅动态代理(dynamic proxying)/拦截器 [文档](Dynamic-Proxying-Interceptors.md). +> 注意, 如果服务类公开了多于一个服务或接口, `OnRegistered` 回调(callback)可能被同一服务类多次调用. 因此, 较安全的方法是使用 `Interceptors.TryAdd` 方法而不是 `Interceptors.Add` 方法. 请参阅动态代理(dynamic proxying)/拦截器 [文档](Dynamic-Proxying-Interceptors.md). ## 第三方提供程序 diff --git a/docs/zh-Hans/Tutorials/Part-10.md b/docs/zh-Hans/Tutorials/Part-10.md index 170e3106a1..da5c23c78f 100644 --- a/docs/zh-Hans/Tutorials/Part-10.md +++ b/docs/zh-Hans/Tutorials/Part-10.md @@ -367,7 +367,7 @@ namespace Acme.BookStore.Books GetListPolicyName = BookStorePermissions.Books.Default; CreatePolicyName = BookStorePermissions.Books.Create; UpdatePolicyName = BookStorePermissions.Books.Edit; - DeletePolicyName = BookStorePermissions.Books.Create; + DeletePolicyName = BookStorePermissions.Books.Delete; } public override async Task GetAsync(Guid id) diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server/Microsoft/AspNetCore/Authentication/Cookies/CookieAuthenticationOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server/Microsoft/AspNetCore/Authentication/Cookies/CookieAuthenticationOptionsExtensions.cs new file mode 100644 index 0000000000..61c064b376 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server/Microsoft/AspNetCore/Authentication/Cookies/CookieAuthenticationOptionsExtensions.cs @@ -0,0 +1,56 @@ +using System; +using IdentityModel.Client; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Authentication.Cookies; + +public static class CookieAuthenticationOptionsExtensions +{ + /// + /// Introspect access token on validating the principal. + /// + /// + /// + /// + public static CookieAuthenticationOptions IntrospectAccessToken(this CookieAuthenticationOptions options, string oidcAuthenticationScheme = "oidc") + { + var originalHandler = options.Events.OnValidatePrincipal; + options.Events.OnValidatePrincipal = async principalContext => + { + originalHandler?.Invoke(principalContext); + + if (principalContext.Principal != null && principalContext.Principal.Identity != null && principalContext.Principal.Identity.IsAuthenticated) + { + var accessToken = principalContext.Properties.GetTokenValue("access_token"); + if (!accessToken.IsNullOrWhiteSpace()) + { + var openIdConnectOptions = principalContext.HttpContext.RequestServices.GetRequiredService>().Get(oidcAuthenticationScheme); + if (openIdConnectOptions.Configuration == null && openIdConnectOptions.ConfigurationManager != null) + { + openIdConnectOptions.Configuration = await openIdConnectOptions.ConfigurationManager.GetConfigurationAsync(principalContext.HttpContext.RequestAborted); + } + + var response = await openIdConnectOptions.Backchannel.IntrospectTokenAsync(new TokenIntrospectionRequest + { + Address = openIdConnectOptions.Configuration?.IntrospectionEndpoint ?? openIdConnectOptions.Authority.EnsureEndsWith('/') + "connect/introspect", + ClientId = openIdConnectOptions.ClientId, + ClientSecret = openIdConnectOptions.ClientSecret, + Token = accessToken + }); + + if (response.IsActive) + { + return; + } + } + + principalContext.RejectPrincipal(); + await principalContext.HttpContext.SignOutAsync(principalContext.Scheme.Name); + } + }; + + return options; + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo.Abp.AspNetCore.Components.Server.csproj b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo.Abp.AspNetCore.Components.Server.csproj index 09c743bc2a..297b2a8576 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo.Abp.AspNetCore.Components.Server.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo.Abp.AspNetCore.Components.Server.csproj @@ -12,11 +12,13 @@ - - - - - + + + + + + + diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Configuration/BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Configuration/BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService.cs new file mode 100644 index 0000000000..40ac508030 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Configuration/BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Volo.Abp.AspNetCore.Components.Web.Configuration; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Components.WebAssembly.Configuration; + +[Dependency(ReplaceServices = true)] +public class BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService : + ICurrentApplicationConfigurationCacheResetService, + ITransientDependency +{ + private readonly WebAssemblyCachedApplicationConfigurationClient _webAssemblyCachedApplicationConfigurationClient; + + public BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService(WebAssemblyCachedApplicationConfigurationClient webAssemblyCachedApplicationConfigurationClient) + { + _webAssemblyCachedApplicationConfigurationClient = webAssemblyCachedApplicationConfigurationClient; + } + + public async Task ResetAsync() + { + await _webAssemblyCachedApplicationConfigurationClient.InitializeAsync(); + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/FlagIconCss/FlagIconCssStyleContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/FlagIconCss/FlagIconCssStyleContributor.cs index 3181d8e1a2..9e5754f393 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/FlagIconCss/FlagIconCssStyleContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/FlagIconCss/FlagIconCssStyleContributor.cs @@ -7,6 +7,13 @@ public class FlagIconCssStyleContributor : BundleContributor { public override void ConfigureBundle(BundleConfigurationContext context) { - context.Files.AddIfNotContains("/libs/flag-icon-css/css/flag-icons.min.css"); + if (context.FileProvider.GetFileInfo("/libs/flag-icons/css/flag-icons.min.css").Exists) + { + context.Files.AddIfNotContains("/libs/flag-icons/css/flag-icons.min.css"); + } + else if (context.FileProvider.GetFileInfo("/libs/flag-icon-css/css/flag-icons.min.css").Exists) + { + context.Files.AddIfNotContains("/libs/flag-icon-css/css/flag-icons.min.css"); + } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Select2/Select2ScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Select2/Select2ScriptContributor.cs index 4818158a97..b18767050b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Select2/Select2ScriptContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Select2/Select2ScriptContributor.cs @@ -1,16 +1,28 @@ using System.Collections.Generic; +using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Packages.Core; using Volo.Abp.Modularity; +using Volo.Abp.Localization; namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Select2; [DependsOn(typeof(CoreScriptContributor))] public class Select2ScriptContributor : BundleContributor { + public const string PackageName = "select2"; public override void ConfigureBundle(BundleConfigurationContext context) { - //TODO: Add select2.full.min.js or localize! + //TODO: Add select2.full.min.js context.Files.AddIfNotContains("/libs/select2/js/select2.min.js"); } + public override void ConfigureDynamicResources(BundleConfigurationContext context) + { + var fileName = context.LazyServiceProvider.LazyGetRequiredService>().Value.GetCurrentUICultureLanguageFilesMap(PackageName); + var filePath = $"/libs/select2/js/i18n/{fileName}.js"; + if (context.FileProvider.GetFileInfo(filePath).Exists) + { + context.Files.AddIfNotContains(filePath); + } + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js index d1cbfa6b6b..6a9e6f7d80 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js @@ -105,6 +105,7 @@ $select.select2({ ajax: { url: url, + delay: 250, dataType: "json", data: function (params) { let query = {}; @@ -132,6 +133,7 @@ width: '100%', dropdownParent: parentSelector ? $(parentSelector) : $('body'), allowClear: allowClear, + language: abp.localization.currentCulture.cultureName, placeholder: { id: '-1', text: placeholder diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpAspNetCoreMvcUiWidgetsModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpAspNetCoreMvcUiWidgetsModule.cs index 54d766ec08..23b3d08602 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpAspNetCoreMvcUiWidgetsModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpAspNetCoreMvcUiWidgetsModule.cs @@ -39,7 +39,7 @@ public class AbpAspNetCoreMvcUiWidgetsModule : AbpModule { var widgetTypes = new List(); - services.OnRegistred(context => + services.OnRegistered(context => { if (WidgetAttribute.IsWidget(context.ImplementationType)) { 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 ec3690ca8e..0a12d0332a 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 @@ -43,6 +43,7 @@ using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.UI; using Volo.Abp.UI.Navigation; +using Volo.Abp.Validation.Localization; namespace Volo.Abp.AspNetCore.Mvc; @@ -174,10 +175,17 @@ public class AbpAspNetCoreMvcModule : AbpModule context.Services.Replace(ServiceDescriptor.Singleton()); context.Services.AddSingleton(); - Configure(mvcOptions => - { - mvcOptions.AddAbp(context.Services); - }); + context.Services.AddOptions() + .Configure((mvcOptions, serviceProvider) => + { + mvcOptions.AddAbp(context.Services); + + // serviceProvider is root service provider. + var stringLocalizer = serviceProvider.GetRequiredService>(); + mvcOptions.ModelBindingMessageProvider.SetValueIsInvalidAccessor(_ => stringLocalizer["The value '{0}' is invalid."]); + mvcOptions.ModelBindingMessageProvider.SetNonPropertyValueMustBeANumberAccessor(() => stringLocalizer["The field must be a number."]); + mvcOptions.ModelBindingMessageProvider.SetValueMustBeANumberAccessor(value => stringLocalizer["The field {0} must be a number.", value]); + }); Configure(options => { diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs index 903e60a16f..13400a7ff9 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs @@ -91,7 +91,7 @@ public class AbpAspNetCoreSignalRModule : AbpModule { var hubTypes = new List(); - services.OnRegistred(context => + services.OnRegistered(context => { if (IsHubClass(context) && !IsDisabledForAutoMap(context)) { diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs index b6527f10c8..7c6fdacc11 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs @@ -24,7 +24,7 @@ public class AbpAuditingModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services.OnRegistred(AuditingInterceptorRegistrar.RegisterIfNeeded); + context.Services.OnRegistered(AuditingInterceptorRegistrar.RegisterIfNeeded); } public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs index 939814cba9..623733e8e5 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq.Expressions; +using System.Threading; using System.Threading.Tasks; namespace Volo.Abp.Auditing; @@ -76,7 +77,8 @@ public class AbpAuditingOptions IgnoredTypes = new List { typeof(Stream), - typeof(Expression) + typeof(Expression), + typeof(CancellationToken) }; EntityHistorySelectors = new EntityHistorySelectorList(); diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs index 9a1a4c52b4..cb244599c2 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs @@ -22,7 +22,7 @@ public class AbpAuthorizationModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services.OnRegistred(AuthorizationInterceptorRegistrar.RegisterIfNeeded); + context.Services.OnRegistered(AuthorizationInterceptorRegistrar.RegisterIfNeeded); AutoAddDefinitionProviders(context.Services); } @@ -64,7 +64,7 @@ public class AbpAuthorizationModule : AbpModule { var definitionProviders = new List(); - services.OnRegistred(context => + services.OnRegistered(context => { if (typeof(IPermissionDefinitionProvider).IsAssignableFrom(context.ImplementationType)) { diff --git a/framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj b/framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj index d8d2b83e5a..e03023a72a 100644 --- a/framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj +++ b/framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobsAbstractionsModule.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobsAbstractionsModule.cs index 99d4d9e4df..8d3dc6caf5 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobsAbstractionsModule.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobsAbstractionsModule.cs @@ -21,7 +21,7 @@ public class AbpBackgroundJobsAbstractionsModule : AbpModule { var jobTypes = new List(); - services.OnRegistred(context => + services.OnRegistered(context => { if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(IBackgroundJob<>)) || ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(IAsyncBackgroundJob<>))) diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AsyncBackgroundJob.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AsyncBackgroundJob.cs index 4cf298f712..aa6cad070a 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AsyncBackgroundJob.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AsyncBackgroundJob.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -6,7 +7,7 @@ namespace Volo.Abp.BackgroundJobs; public abstract class AsyncBackgroundJob : IAsyncBackgroundJob { - //TODO: Add UOW, Localization and other useful properties..? + //TODO: Add UOW, Localization, CancellationTokenProvider and other useful properties..? public ILogger> Logger { get; set; } diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJob.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJob.cs index 57beeb77b5..b1770942fe 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJob.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJob.cs @@ -1,3 +1,4 @@ +using System.Threading; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -5,7 +6,7 @@ namespace Volo.Abp.BackgroundJobs; public abstract class BackgroundJob : IBackgroundJob { - //TODO: Add UOW, Localization and other useful properties..? + //TODO: Add UOW, Localization, CancellationTokenProvider and other useful properties..? public ILogger> Logger { get; set; } diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobExecuter.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobExecuter.cs index 7a6f54fbd7..7798a8d9a6 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobExecuter.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobExecuter.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; using Volo.Abp.ExceptionHandling; using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; namespace Volo.Abp.BackgroundJobs; @@ -46,13 +47,19 @@ public class BackgroundJobExecuter : IBackgroundJobExecuter, ITransientDependenc { using(CurrentTenant.Change(GetJobArgsTenantId(context.JobArgs))) { - if (jobExecuteMethod.Name == nameof(IAsyncBackgroundJob.ExecuteAsync)) - { - await ((Task)jobExecuteMethod.Invoke(job, new[] { context.JobArgs })); - } - else + var cancellationTokenProvider = + context.ServiceProvider.GetRequiredService(); + + using (cancellationTokenProvider.Use(context.CancellationToken)) { - jobExecuteMethod.Invoke(job, new[] { context.JobArgs }); + if (jobExecuteMethod.Name == nameof(IAsyncBackgroundJob.ExecuteAsync)) + { + await ((Task)jobExecuteMethod.Invoke(job, new[] { context.JobArgs })); + } + else + { + jobExecuteMethod.Invoke(job, new[] { context.JobArgs }); + } } } diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IAsyncBackgroundJob.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IAsyncBackgroundJob.cs index dc39b1bd0f..022067d3ed 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IAsyncBackgroundJob.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IAsyncBackgroundJob.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; namespace Volo.Abp.BackgroundJobs; diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IBackgroundJob.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IBackgroundJob.cs index 140db1e72a..8c8c17ca93 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IBackgroundJob.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IBackgroundJob.cs @@ -1,4 +1,6 @@ -namespace Volo.Abp.BackgroundJobs; +using System.Threading; + +namespace Volo.Abp.BackgroundJobs; /// /// Defines interface of a background job. diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/JobExecutionContext.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/JobExecutionContext.cs index a5de60d298..ae7e098070 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/JobExecutionContext.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/JobExecutionContext.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Volo.Abp.DependencyInjection; namespace Volo.Abp.BackgroundJobs; @@ -11,10 +12,17 @@ public class JobExecutionContext : IServiceProviderAccessor public object JobArgs { get; } - public JobExecutionContext(IServiceProvider serviceProvider, Type jobType, object jobArgs) + public CancellationToken CancellationToken { get; } + + public JobExecutionContext( + IServiceProvider serviceProvider, + Type jobType, + object jobArgs, + CancellationToken cancellationToken = default) { ServiceProvider = serviceProvider; JobType = jobType; JobArgs = jobArgs; + CancellationToken = cancellationToken; } } diff --git a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs index 2583ada9fc..a94426adad 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs @@ -12,22 +12,22 @@ namespace Volo.Abp.BackgroundJobs.Hangfire; public class HangfireBackgroundJobManager : IBackgroundJobManager, ITransientDependency { protected AbpBackgroundJobOptions Options { get; } - + public HangfireBackgroundJobManager(IOptions options) { Options = options.Value; } - + public virtual Task EnqueueAsync(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null) { return Task.FromResult(delay.HasValue ? BackgroundJob.Schedule>( - adapter => adapter.ExecuteAsync(GetQueueName(typeof(TArgs)),args), + adapter => adapter.ExecuteAsync(GetQueueName(typeof(TArgs)), args, default), delay.Value ) : BackgroundJob.Enqueue>( - adapter => adapter.ExecuteAsync(GetQueueName(typeof(TArgs)) ,args) + adapter => adapter.ExecuteAsync(GetQueueName(typeof(TArgs)), args, default) )); } diff --git a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs index 7ba0cd7db4..58d2863618 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; using Hangfire; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -21,8 +22,8 @@ public class HangfireJobExecutionAdapter Options = options.Value; } - [Queue("{0}")] - public async Task ExecuteAsync(string queue, TArgs args) + [Queue("{0}")] + public async Task ExecuteAsync(string queue, TArgs args, CancellationToken cancellationToken = default) { if (!Options.IsJobExecutionEnabled) { @@ -38,7 +39,7 @@ public class HangfireJobExecutionAdapter using (var scope = ServiceScopeFactory.CreateScope()) { var jobType = Options.GetJob(typeof(TArgs)).JobType; - var context = new JobExecutionContext(scope.ServiceProvider, jobType, args); + var context = new JobExecutionContext(scope.ServiceProvider, jobType, args, cancellationToken: cancellationToken); await JobExecuter.ExecuteAsync(context); } } diff --git a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs index 3127cc5910..588e844153 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs @@ -40,7 +40,7 @@ public class QuartzJobExecutionAdapter : IJob { var args = JsonSerializer.Deserialize(context.JobDetail.JobDataMap.GetString(nameof(TArgs))); var jobType = Options.GetJob(typeof(TArgs)).JobType; - var jobContext = new JobExecutionContext(scope.ServiceProvider, jobType, args); + var jobContext = new JobExecutionContext(scope.ServiceProvider, jobType, args, cancellationToken: context.CancellationToken); try { await JobExecuter.ExecuteAsync(jobContext); diff --git a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs index 8de0a5ff6e..4aeaf59885 100644 --- a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs +++ b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobWorker.cs @@ -68,7 +68,8 @@ public class BackgroundJobWorker : AsyncPeriodicBackgroundWorkerBase, IBackgroun var context = new JobExecutionContext( workerContext.ServiceProvider, jobConfiguration.JobType, - jobArgs); + jobArgs, + workerContext.CancellationToken); try { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs index cc198e877b..ec2074434a 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs @@ -58,7 +58,7 @@ public class AuthService : IAuthService, ITransientDependency { if (!response.IsSuccessStatusCode) { - Logger.LogError("Remote server returns '{response.StatusCode}'"); + Logger.LogError($"Remote server returns '{response.StatusCode}'"); return null; } @@ -127,6 +127,26 @@ public class AuthService : IAuthService, ITransientDependency } } + public async Task CheckMultipleOrganizationsAsync(string username) + { + var url = $"{CliUrls.WwwAbpIo}api/license/check-multiple-organizations?username={username}"; + + var client = CliHttpClientFactory.CreateClient(); + + using (var response = await client.GetHttpResponseMessageWithRetryAsync(url, CancellationTokenProvider.Token, Logger)) + { + if (!response.IsSuccessStatusCode) + { + throw new Exception($"ERROR: Remote server returns '{response.StatusCode}'"); + } + + await RemoteServiceExceptionHandler.EnsureSuccessfulHttpResponseAsync(response); + + var responseContent = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize(responseContent); + } + } + private async Task LogoutAsync(string accessToken) { try diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/IAuthService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/IAuthService.cs index ad2916ca0b..f91fd0dc00 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/IAuthService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/IAuthService.cs @@ -9,4 +9,6 @@ public interface IAuthService Task LoginAsync(string userName, string password, string organizationName = null); Task LogoutAsync(); + + Task CheckMultipleOrganizationsAsync(string username); } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/LoginInfo.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/LoginInfo.cs index aacb26e5f4..3b14c96330 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/LoginInfo.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/LoginInfo.cs @@ -1,8 +1,11 @@ - +using System; + namespace Volo.Abp.Cli.Auth; public class LoginInfo { + public Guid? Id { get; set; } + public string Name { get; set; } public string Surname { get; set; } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginCommand.cs index a3b6d4a411..0d5b3b31e5 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginCommand.cs @@ -2,13 +2,11 @@ using Microsoft.Extensions.Logging.Abstractions; using System; using System.Text; -using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; using Volo.Abp.Cli.Args; using Volo.Abp.Cli.Auth; -using Volo.Abp.Cli.Http; using Volo.Abp.Cli.ProjectBuilding; using Volo.Abp.Cli.Utils; using Volo.Abp.DependencyInjection; @@ -26,17 +24,13 @@ public class LoginCommand : IConsoleCommand, ITransientDependency public ICancellationTokenProvider CancellationTokenProvider { get; } public IRemoteServiceExceptionHandler RemoteServiceExceptionHandler { get; } - private readonly CliHttpClientFactory _cliHttpClientFactory; - public LoginCommand(AuthService authService, ICancellationTokenProvider cancellationTokenProvider, - IRemoteServiceExceptionHandler remoteServiceExceptionHandler, - CliHttpClientFactory cliHttpClientFactory) + IRemoteServiceExceptionHandler remoteServiceExceptionHandler) { AuthService = authService; CancellationTokenProvider = cancellationTokenProvider; RemoteServiceExceptionHandler = remoteServiceExceptionHandler; - _cliHttpClientFactory = cliHttpClientFactory; Logger = NullLogger.Instance; } @@ -111,7 +105,7 @@ public class LoginCommand : IConsoleCommand, ITransientDependency private async Task HasMultipleOrganizationAndThisNotSpecified(CommandLineArgs commandLineArgs, string organization) { if (string.IsNullOrWhiteSpace(organization) && - await CheckMultipleOrganizationsAsync(commandLineArgs.Target)) + await AuthService.CheckMultipleOrganizationsAsync(commandLineArgs.Target)) { Logger.LogError($"You have multiple organizations, please specify your organization with `--organization` parameter."); return true; @@ -168,26 +162,6 @@ public class LoginCommand : IConsoleCommand, ITransientDependency return false; } - private async Task CheckMultipleOrganizationsAsync(string username) - { - var url = $"{CliUrls.WwwAbpIo}api/license/check-multiple-organizations?username={username}"; - - var client = _cliHttpClientFactory.CreateClient(); - - using (var response = await client.GetHttpResponseMessageWithRetryAsync(url, CancellationTokenProvider.Token, Logger)) - { - if (!response.IsSuccessStatusCode) - { - throw new Exception($"ERROR: Remote server returns '{response.StatusCode}'"); - } - - await RemoteServiceExceptionHandler.EnsureSuccessfulHttpResponseAsync(response); - - var responseContent = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(responseContent); - } - } - public string GetUsageInfo() { var sb = new StringBuilder(); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginInfoCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginInfoCommand.cs index 9d17bf4c4f..d8f007dc2c 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginInfoCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginInfoCommand.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using System.Text; using System.Threading.Tasks; @@ -41,7 +40,7 @@ public class LoginInfoCommand : IConsoleCommand, ITransientDependency var sb = new StringBuilder(); sb.AppendLine(""); - sb.AppendLine($"Login info:"); + sb.AppendLine("Login info:"); sb.AppendLine($"Name: {loginInfo.Name}"); sb.AppendLine($"Surname: {loginInfo.Surname}"); sb.AppendLine($"Username: {loginInfo.Username}"); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SuiteAppSettingsService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SuiteAppSettingsService.cs new file mode 100644 index 0000000000..d274234ded --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SuiteAppSettingsService.cs @@ -0,0 +1,94 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using Volo.Abp.Cli.Utils; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands.Services; + +public class SuiteAppSettingsService : ITransientDependency +{ + private const int DefaultPort = 3000; + + public CmdHelper CmdHelper { get; } + + public SuiteAppSettingsService(CmdHelper cmdHelper) + { + CmdHelper = cmdHelper; + } + + public async Task GetSuitePortAsync() + { + return await GetSuitePortAsync(GetCurrentSuiteVersion()); + } + + public async Task GetSuitePortAsync(string version) + { + var filePath = GetFilePathOrNull(version); + + if (filePath == null) + { + return DefaultPort; + } + + var content = File.ReadAllText(filePath); + + var contentAsJson = JObject.Parse(content); + + var url = contentAsJson["AbpSuite"]?["ApplicationUrl"]?.ToString(); + + if (url == null) + { + return DefaultPort; + } + + return Convert.ToInt32(url.Split(":").Last()); + } + + private string GetFilePathOrNull(string version) + { + if (version == null) + { + return null; + } + + var path = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), + ".dotnet", + "tools", + ".store", + "volo.abp.suite", + version, + "volo.abp.suite", + version, + "tools", + "net7.0", + "any", + "appsettings.json" + ); + + if (!File.Exists(path)) + { + return null; + } + + return path; + } + + private string GetCurrentSuiteVersion() + { + var dotnetToolList = CmdHelper.RunCmdAndGetOutput("dotnet tool list -g", out int exitCode); + + var suiteLine = dotnetToolList.Split(Environment.NewLine) + .FirstOrDefault(l => l.ToLower().StartsWith("volo.abp.suite ")); + + if (string.IsNullOrEmpty(suiteLine)) + { + return null; + } + + return suiteLine.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1]; + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs index 83c21d4773..b35efb6f36 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs @@ -35,23 +35,26 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency private readonly PackageVersionCheckerService _packageVersionCheckerService; private readonly AuthService _authService; private readonly CliHttpClientFactory _cliHttpClientFactory; + private readonly SuiteAppSettingsService _suiteAppSettingsService; private const string SuitePackageName = "Volo.Abp.Suite"; public ILogger Logger { get; set; } - private const string AbpSuiteHost = "http://localhost:3000"; + private int _abpSuitePort = 3000; public SuiteCommand( AbpNuGetIndexUrlService nuGetIndexUrlService, PackageVersionCheckerService packageVersionCheckerService, ICmdHelper cmdHelper, AuthService authService, - CliHttpClientFactory cliHttpClientFactory) + CliHttpClientFactory cliHttpClientFactory, + SuiteAppSettingsService suiteAppSettingsService) { CmdHelper = cmdHelper; _nuGetIndexUrlService = nuGetIndexUrlService; _packageVersionCheckerService = packageVersionCheckerService; _authService = authService; _cliHttpClientFactory = cliHttpClientFactory; + _suiteAppSettingsService = suiteAppSettingsService; Logger = NullLogger.Instance; } @@ -72,17 +75,20 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency commandLineArgs.Options.ContainsKey(Options.Preview.Long); var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long); + var currentSuiteVersionAsString = GetCurrentSuiteVersion(); switch (operationType) { case "": case null: - await InstallSuiteIfNotInstalledAsync(); + await InstallSuiteIfNotInstalledAsync(currentSuiteVersionAsString); + _abpSuitePort = await _suiteAppSettingsService.GetSuitePortAsync(currentSuiteVersionAsString); RunSuite(); break; case "generate": - await InstallSuiteIfNotInstalledAsync(); + await InstallSuiteIfNotInstalledAsync(currentSuiteVersionAsString); + _abpSuitePort = await _suiteAppSettingsService.GetSuitePortAsync(currentSuiteVersionAsString); var suiteProcess = StartSuite(); System.Threading.Thread.Sleep(500); //wait for initialization of the app await GenerateCrudPageAsync(commandLineArgs); @@ -130,7 +136,7 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency } var IsSolutionBuiltResponse = await client.GetAsync( - $"{AbpSuiteHost}/api/abpSuite/solutions/{solutionId.ToString()}/is-built" + $"http://localhost:{_abpSuitePort}/api/abpSuite/solutions/{solutionId.ToString()}/is-built" ); var IsSolutionBuilt = Convert.ToBoolean(await IsSolutionBuiltResponse.Content.ReadAsStringAsync()); @@ -148,7 +154,7 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency ); var responseMessage = await client.PostAsync( - $"{AbpSuiteHost}/api/abpSuite/crudPageGenerator/{solutionId.ToString()}/save-and-generate-entity", + $"http://localhost:{_abpSuitePort}/api/abpSuite/crudPageGenerator/{solutionId.ToString()}/save-and-generate-entity", entityContent ); @@ -178,7 +184,7 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency } var responseMessage = await client.GetHttpResponseMessageWithRetryAsync( - "http://localhost:3000/api/abpSuite/solutions", + $"http://localhost:{_abpSuitePort}/api/abpSuite/solutions", _cliHttpClientFactory.GetCancellationToken(TimeSpan.FromMinutes(10)), Logger, timeIntervals.ToArray()); @@ -216,7 +222,7 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency ); var responseMessage = await client.PostAsync( - "http://localhost:3000/api/abpSuite/addSolution", + $"http://localhost:{_abpSuitePort}/api/abpSuite/addSolution", entityContent, _cliHttpClientFactory.GetCancellationToken(TimeSpan.FromMinutes(10)) ); @@ -234,11 +240,9 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency } } - private async Task InstallSuiteIfNotInstalledAsync() + private async Task InstallSuiteIfNotInstalledAsync(string currentSuiteVersion) { - var currentSuiteVersionAsString = GetCurrentSuiteVersion(); - - if (string.IsNullOrEmpty(currentSuiteVersionAsString)) + if (string.IsNullOrEmpty(currentSuiteVersion)) { await InstallSuiteAsync(); } @@ -430,6 +434,19 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency Logger.LogWarning("Couldn't check ABP Suite installed status: " + ex.Message); } + if (IsSuiteAlreadyRunning()) + { + Logger.LogInformation("Opening suite..."); + CmdHelper.Open($"http://localhost:{_abpSuitePort}"); + return; + } + + if (IsPortAlreadyInUse()) + { + Logger.LogError($"Port \"{_abpSuitePort}\" is already in use."); + return; + } + CmdHelper.RunCmd("abp-suite"); } @@ -453,23 +470,32 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency return null; } + if (IsPortAlreadyInUse()) + { + Logger.LogError($"Port \"{_abpSuitePort}\" is already in use."); + return null; + } + return CmdHelper.RunCmdAndGetProcess("abp-suite --no-browser"); } private bool IsSuiteAlreadyRunning() + { + return GetProcessesRelatedWithSuite().Any(); + } + + private bool IsPortAlreadyInUse() { var ipGP = IPGlobalProperties.GetIPGlobalProperties(); var endpoints = ipGP.GetActiveTcpListeners(); - return endpoints.Any(e => e.Port == 3000); + return endpoints.Any(e => e.Port == _abpSuitePort); } private void KillSuite() { try { - var suiteProcesses = (from p in Process.GetProcesses() - where p.ProcessName.ToLower().Contains("abp-suite") - select p); + var suiteProcesses = GetProcessesRelatedWithSuite(); foreach (var suiteProcess in suiteProcesses) { @@ -483,6 +509,13 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency } } + private IEnumerable GetProcessesRelatedWithSuite() + { + return (from p in Process.GetProcesses() + where p.ProcessName.ToLower().Contains("abp-suite") + select p); + } + public string GetUsageInfo() { var sb = new StringBuilder(); diff --git a/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationActionExtensions.cs b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationActionExtensions.cs index 2947526fa5..286cc05210 100644 --- a/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationActionExtensions.cs +++ b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationActionExtensions.cs @@ -5,9 +5,9 @@ namespace Microsoft.Extensions.DependencyInjection; public static class ServiceCollectionRegistrationActionExtensions { - // OnRegistred + // OnRegistered - public static void OnRegistred(this IServiceCollection services, Action registrationAction) + public static void OnRegistered(this IServiceCollection services, Action registrationAction) { GetOrCreateRegistrationActionList(services).Add(registrationAction); } diff --git a/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs b/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs index c7e1bef3b2..57f2c59d57 100644 --- a/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs +++ b/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs @@ -209,7 +209,22 @@ public static class AbpStringExtensions return str; } - return str.Substring(0, pos) + replace + str.Substring(pos + search.Length); + var searchLength = search.Length; + var replaceLength = replace.Length; + var newLength = str.Length - searchLength + replaceLength; + + Span buffer = newLength <= 1024 ? stackalloc char[newLength] : new char[newLength]; + + // Copy the part of the original string before the search term + str.AsSpan(0, pos).CopyTo(buffer); + + // Copy the replacement text + replace.AsSpan().CopyTo(buffer.Slice(pos)); + + // Copy the remainder of the original string + str.AsSpan(pos + searchLength).CopyTo(buffer.Slice(pos + replaceLength)); + + return buffer.ToString(); } /// diff --git a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs index 931dc8c07e..b573d7d958 100644 --- a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs +++ b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs @@ -42,7 +42,7 @@ public class AbpDataModule : AbpModule { var contributors = new List(); - services.OnRegistred(context => + services.OnRegistered(context => { if (typeof(IDataSeedContributor).IsAssignableFrom(context.ImplementationType)) { 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 881a6437d9..9a70469df8 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJob.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJob.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.BackgroundJobs; using Volo.Abp.DependencyInjection; diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs index fe3d9f3032..21e663e5e1 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs @@ -47,7 +47,7 @@ public class AbpEventBusModule : AbpModule var localHandlers = new List(); var distributedHandlers = new List(); - services.OnRegistred(context => + services.OnRegistered(context => { if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(ILocalEventHandler<>))) { diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs index 7411c61fdb..cde88956b5 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs @@ -22,7 +22,7 @@ public class AbpFeaturesModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services.OnRegistred(FeatureInterceptorRegistrar.RegisterIfNeeded); + context.Services.OnRegistered(FeatureInterceptorRegistrar.RegisterIfNeeded); AutoAddDefinitionProviders(context.Services); } @@ -57,7 +57,7 @@ public class AbpFeaturesModule : AbpModule { var definitionProviders = new List(); - services.OnRegistred(context => + services.OnRegistered(context => { if (typeof(IFeatureDefinitionProvider).IsAssignableFrom(context.ImplementationType)) { diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs index 14cd0d405b..1a1e2ece3b 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs @@ -124,7 +124,7 @@ public class FeatureDefinition : ICanCreateChildFeature } /// - /// Sets a property in the dictionary. + /// Adds one or more providers to the list. /// This is a shortcut for nested calls on this object. /// public virtual FeatureDefinition WithProviders(params string[] providers) diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/AbpGlobalFeaturesModule.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/AbpGlobalFeaturesModule.cs index 8541d04306..3c8cd5708a 100644 --- a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/AbpGlobalFeaturesModule.cs +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/AbpGlobalFeaturesModule.cs @@ -17,7 +17,7 @@ public class AbpGlobalFeaturesModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services.OnRegistred(GlobalFeatureInterceptorRegistrar.RegisterIfNeeded); + context.Services.OnRegistered(GlobalFeatureInterceptorRegistrar.RegisterIfNeeded); } public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/framework/src/Volo.Abp.Http.Client.Web/Volo/Abp/Http/Client/Web/Conventions/AbpHttpClientProxyServiceConvention.cs b/framework/src/Volo.Abp.Http.Client.Web/Volo/Abp/Http/Client/Web/Conventions/AbpHttpClientProxyServiceConvention.cs index d44a294799..49080939c2 100644 --- a/framework/src/Volo.Abp.Http.Client.Web/Volo/Abp/Http/Client/Web/Conventions/AbpHttpClientProxyServiceConvention.cs +++ b/framework/src/Volo.Abp.Http.Client.Web/Volo/Abp/Http/Client/Web/Conventions/AbpHttpClientProxyServiceConvention.cs @@ -6,6 +6,7 @@ using System.Reflection; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ActionConstraints; using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Volo.Abp.Application.Services; @@ -13,6 +14,7 @@ using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Client.ClientProxying; +using Volo.Abp.Http.Client.StaticProxying; using Volo.Abp.Http.Modeling; using Volo.Abp.Reflection; @@ -24,14 +26,17 @@ public class AbpHttpClientProxyServiceConvention : AbpServiceConvention protected readonly IClientProxyApiDescriptionFinder ClientProxyApiDescriptionFinder; protected readonly List ControllerWithAttributeRoute; protected readonly List ActionWithAttributeRoute; + protected readonly AbpHttpClientStaticProxyingOptions StaticProxyingOptions; public AbpHttpClientProxyServiceConvention( IOptions options, IConventionalRouteBuilder conventionalRouteBuilder, - IClientProxyApiDescriptionFinder clientProxyApiDescriptionFinder) + IClientProxyApiDescriptionFinder clientProxyApiDescriptionFinder, + IOptions staticProxyingOptions) : base(options, conventionalRouteBuilder) { ClientProxyApiDescriptionFinder = clientProxyApiDescriptionFinder; + StaticProxyingOptions = staticProxyingOptions.Value; ControllerWithAttributeRoute = new List(); ActionWithAttributeRoute = new List(); } @@ -73,6 +78,27 @@ public class AbpHttpClientProxyServiceConvention : AbpServiceConvention } } + protected override void ConfigureParameters(ControllerModel controller) + { + foreach (var action in controller.Actions) + { + foreach (var prm in action.Parameters) + { + if (prm.BindingInfo != null) + { + continue; + } + + if (StaticProxyingOptions.BindingFromQueryTypes.Contains(prm.ParameterInfo.ParameterType)) + { + prm.BindingInfo = BindingInfo.GetBindingInfo(new[] { new FromQueryAttribute() }); + } + } + } + + base.ConfigureParameters(controller); + } + protected virtual bool ShouldBeRemove(ApplicationModel application, ControllerModel controllerModel) { return application.Controllers diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/StaticProxying/AbpHttpClientStaticProxyingOptions.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/StaticProxying/AbpHttpClientStaticProxyingOptions.cs new file mode 100644 index 0000000000..f7c74303a9 --- /dev/null +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/StaticProxying/AbpHttpClientStaticProxyingOptions.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Http.Client.StaticProxying; + +public class AbpHttpClientStaticProxyingOptions +{ + public List BindingFromQueryTypes { get; } + + public AbpHttpClientStaticProxyingOptions() + { + BindingFromQueryTypes = new List(); + } +} diff --git a/framework/src/Volo.Abp.MultiLingualObjects/Volo.Abp.MultiLingualObjects.csproj b/framework/src/Volo.Abp.MultiLingualObjects/Volo.Abp.MultiLingualObjects.csproj index da83bc80f4..77da44eb47 100644 --- a/framework/src/Volo.Abp.MultiLingualObjects/Volo.Abp.MultiLingualObjects.csproj +++ b/framework/src/Volo.Abp.MultiLingualObjects/Volo.Abp.MultiLingualObjects.csproj @@ -1,20 +1,21 @@ - - - - - - - netstandard2.0 - Volo.Abp.MultiLingualObject - $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; - false - false - false - - - - - - - - + + + + + + + netstandard2.0 + Volo.Abp.MultiLingualObject + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + enable + + + + + + + diff --git a/framework/src/Volo.Abp.MultiLingualObjects/Volo/Abp/MultiLingualObjects/IMultiLingualObjectManager.cs b/framework/src/Volo.Abp.MultiLingualObjects/Volo/Abp/MultiLingualObjects/IMultiLingualObjectManager.cs index 904f3abd32..4edbd984bd 100644 --- a/framework/src/Volo.Abp.MultiLingualObjects/Volo/Abp/MultiLingualObjects/IMultiLingualObjectManager.cs +++ b/framework/src/Volo.Abp.MultiLingualObjects/Volo/Abp/MultiLingualObjects/IMultiLingualObjectManager.cs @@ -5,16 +5,30 @@ namespace Volo.Abp.MultiLingualObjects; public interface IMultiLingualObjectManager { - Task GetTranslationAsync( + Task GetTranslationAsync( TMultiLingual multiLingual, - string culture = null, + string? culture = null, bool fallbackToParentCultures = true) where TMultiLingual : IMultiLingualObject where TTranslation : class, IObjectTranslation; - Task GetTranslationAsync( - ICollection translations, - string culture = null, + Task GetTranslationAsync( + IEnumerable translations, + string? culture = null, bool fallbackToParentCultures = true) + where TTranslation : class, IObjectTranslation; + + + Task> GetBulkTranslationsAsync( + IEnumerable> translationsCombined, + string? culture = null, + bool fallbackToParentCultures = true) + where TTranslation : class, IObjectTranslation; + + Task> GetBulkTranslationsAsync( + IEnumerable multiLinguals, + string? culture = null, + bool fallbackToParentCultures = true) + where TMultiLingual : IMultiLingualObject where TTranslation : class, IObjectTranslation; } diff --git a/framework/src/Volo.Abp.MultiLingualObjects/Volo/Abp/MultiLingualObjects/MultiLingualObjectManager.cs b/framework/src/Volo.Abp.MultiLingualObjects/Volo/Abp/MultiLingualObjects/MultiLingualObjectManager.cs index afb4509917..908431e997 100644 --- a/framework/src/Volo.Abp.MultiLingualObjects/Volo/Abp/MultiLingualObjects/MultiLingualObjectManager.cs +++ b/framework/src/Volo.Abp.MultiLingualObjects/Volo/Abp/MultiLingualObjects/MultiLingualObjectManager.cs @@ -19,16 +19,16 @@ public class MultiLingualObjectManager : IMultiLingualObjectManager, ITransientD { SettingProvider = settingProvider; } - public virtual async Task GetTranslationAsync( - ICollection translations, - string culture, + public virtual async Task GetTranslationAsync( + IEnumerable translations, + string? culture, bool fallbackToParentCultures) where TTranslation : class, IObjectTranslation { culture ??= CultureInfo.CurrentUICulture.Name; - if (translations.IsNullOrEmpty()) + if (translations == null || !translations.Any()) { return null; } @@ -65,9 +65,9 @@ public class MultiLingualObjectManager : IMultiLingualObjectManager, ITransientD return translation; } - public virtual Task GetTranslationAsync( + public virtual Task GetTranslationAsync( TMultiLingual multiLingual, - string culture = null, + string? culture = null, bool fallbackToParentCultures = true) where TMultiLingual : IMultiLingualObject where TTranslation : class, IObjectTranslation @@ -75,13 +75,13 @@ public class MultiLingualObjectManager : IMultiLingualObjectManager, ITransientD return GetTranslationAsync(multiLingual.Translations, culture: culture, fallbackToParentCultures: fallbackToParentCultures); } - protected virtual TTranslation GetTranslationBasedOnCulturalRecursive( - CultureInfo culture, ICollection translations, int currentDepth) + protected virtual TTranslation? GetTranslationBasedOnCulturalRecursive( + CultureInfo culture, IEnumerable translations, int currentDepth) where TTranslation : class, IObjectTranslation { if (culture == null || culture.Name.IsNullOrWhiteSpace() || - translations.IsNullOrEmpty() || + translations == null || !translations.Any() || currentDepth > MaxCultureFallbackDepth) { return null; @@ -89,5 +89,108 @@ public class MultiLingualObjectManager : IMultiLingualObjectManager, ITransientD var translation = translations.FirstOrDefault(pt => pt.Language.Equals(culture.Name, StringComparison.OrdinalIgnoreCase)); return translation ?? GetTranslationBasedOnCulturalRecursive(culture.Parent, translations, currentDepth + 1); - } + } + + public virtual async Task> GetBulkTranslationsAsync(IEnumerable> translationsCombined, string? culture, bool fallbackToParentCultures) + where TTranslation : class, IObjectTranslation + { + culture ??= CultureInfo.CurrentUICulture.Name; + + if (translationsCombined == null || !translationsCombined.Any()) + { + return new(); + } + + var someHaveNoTranslations = false; + var res = new List(); + foreach (var translations in translationsCombined) + { + if (!translations.Any()) + { + //if the src has no translations, don't try to find a translation + res.Add(null); + continue; + } + var translation = translations.FirstOrDefault(pt => pt.Language == culture); + if (translation != null) + { + res.Add(translation); + } + else + { + if (fallbackToParentCultures) + { + translation = GetTranslationBasedOnCulturalRecursive( + CultureInfo.CurrentUICulture.Parent, + translations, + 0 + ); + + if (translation != null) + { + res.Add(translation); + } + else + { + res.Add(null); + someHaveNoTranslations = true; + } + } + else + { + res.Add(null); + someHaveNoTranslations = true; + } + } + } + + + if (someHaveNoTranslations) + { + var defaultLanguage = await SettingProvider.GetOrNullAsync(LocalizationSettingNames.DefaultLanguage); + + var index = 0; + foreach (var translations in translationsCombined) + { + if (!translations.Any()) + { + //don't try to find a translation + } + else + { + var translation = res[index]; + if (translation != null) + { + continue; + } + translation = translations.FirstOrDefault(pt => pt.Language == defaultLanguage); + if (translation != null) + { + res[index] = translation; + } + else + { + res[index] = translations.FirstOrDefault(); + } + } + index++; + } + } + return res; + } + + public virtual async Task> GetBulkTranslationsAsync(IEnumerable multiLinguals, string? culture, bool fallbackToParentCultures) + where TMultiLingual : IMultiLingualObject + where TTranslation : class, IObjectTranslation + { + var resInitial = await GetBulkTranslationsAsync(multiLinguals.Select(x => x.Translations), culture, fallbackToParentCultures); + var index = 0; + var res = new List<(TMultiLingual entity, TTranslation? translation)>(); + foreach (var item in multiLinguals) + { + var t = resInitial[index++]; + res.Add((item, t)); + } + return res; + } } diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/AbpSecurityModule.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/AbpSecurityModule.cs index 75ca252ca7..921aca8d7e 100644 --- a/framework/src/Volo.Abp.Security/Volo/Abp/Security/AbpSecurityModule.cs +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/AbpSecurityModule.cs @@ -63,7 +63,7 @@ public class AbpSecurityModule : AbpModule { var contributorTypes = new List(); - services.OnRegistred(context => + services.OnRegistered(context => { if (typeof(IAbpClaimsPrincipalContributor).IsAssignableFrom(context.ImplementationType)) { diff --git a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs index ebaaef54e1..eb05fbb949 100644 --- a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs +++ b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs @@ -36,7 +36,7 @@ public class AbpSettingsModule : AbpModule { var definitionProviders = new List(); - services.OnRegistred(context => + services.OnRegistered(context => { if (typeof(ISettingDefinitionProvider).IsAssignableFrom(context.ImplementationType)) { diff --git a/framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/AbpTextTemplatingCoreModule.cs b/framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/AbpTextTemplatingCoreModule.cs index 74f0589355..cce61484b3 100644 --- a/framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/AbpTextTemplatingCoreModule.cs +++ b/framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/AbpTextTemplatingCoreModule.cs @@ -23,7 +23,7 @@ public class AbpTextTemplatingCoreModule : AbpModule var definitionProviders = new List(); var contentContributors = new List(); - services.OnRegistred(context => + services.OnRegistered(context => { if (typeof(ITemplateDefinitionProvider).IsAssignableFrom(context.ImplementationType)) { diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs index 1a69de7f2c..9f48aebf97 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using JetBrains.Annotations; using Volo.Abp.Data; @@ -5,7 +6,7 @@ using Volo.Abp.UI.Navigation; namespace Volo.Abp.UI.Navigation; -public class ApplicationMenu : IHasMenuItems +public class ApplicationMenu : IHasMenuItems, IHasMenuGroups { /// /// Unique name of the menu in the application. @@ -31,6 +32,10 @@ public class ApplicationMenu : IHasMenuItems [NotNull] public ApplicationMenuItemList Items { get; } + /// + [NotNull] + public ApplicationMenuGroupList Groups { get; } + /// /// Can be used to store a custom object related to this menu. /// @@ -47,6 +52,7 @@ public class ApplicationMenu : IHasMenuItems DisplayName = displayName ?? Name; Items = new ApplicationMenuItemList(); + Groups = new ApplicationMenuGroupList(); } /// @@ -60,6 +66,17 @@ public class ApplicationMenu : IHasMenuItems return this; } + /// + /// Adds a to . + /// + /// to be added + /// This object + public ApplicationMenu AddGroup([NotNull] ApplicationMenuGroup group) + { + Groups.Add(group); + return this; + } + /// /// Adds a custom data item to with given key & value. /// diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs index eec549cf9c..624abde425 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs @@ -64,4 +64,54 @@ public static class ApplicationMenuExtensions return menuWithItems; } + + [NotNull] + public static ApplicationMenuGroup GetMenuGroup( + [NotNull] this IHasMenuGroups menuWithGroups, + string groupName) + { + var menuGroup = menuWithGroups.GetMenuGroupOrNull(groupName); + if (menuGroup == null) + { + throw new AbpException($"Could not find a group item with given name: {groupName}"); + } + + return menuGroup; + } + + [CanBeNull] + public static ApplicationMenuGroup GetMenuGroupOrNull( + [NotNull] this IHasMenuGroups menuWithGroups, + string menuGroupName) + { + Check.NotNull(menuWithGroups, nameof(menuWithGroups)); + + return menuWithGroups.Groups.FirstOrDefault(group => group.Name == menuGroupName); + } + + public static bool TryRemoveMenuGroup( + [NotNull] this IHasMenuGroups menuWithGroups, + string menuGroupName) + { + Check.NotNull(menuWithGroups, nameof(menuWithGroups)); + + return menuWithGroups.Groups.RemoveAll(group => group.Name == menuGroupName) > 0; + } + + [NotNull] + public static IHasMenuGroups SetMenuGroupOrder( + [NotNull] this IHasMenuGroups menuWithGroups, + string menuGroupName, + int order) + { + Check.NotNull(menuWithGroups, nameof(menuWithGroups)); + + var menuGroup = menuWithGroups.GetMenuGroupOrNull(menuGroupName); + if (menuGroup != null) + { + menuGroup.Order = order; + } + + return menuWithGroups; + } } diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroup.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroup.cs new file mode 100644 index 0000000000..469c0ad355 --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroup.cs @@ -0,0 +1,67 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.UI.Navigation; + +public class ApplicationMenuGroup +{ + private string _displayName; + + /// + /// Default value of a group item. + /// + public const int DefaultOrder = 1000; + + /// + /// Unique name of the group in the application. + /// + [NotNull] + public string Name { get; } + + /// + /// Display name of the group. + /// + [NotNull] + public string DisplayName { + get { return _displayName; } + set { + Check.NotNullOrWhiteSpace(value, nameof(value)); + _displayName = value; + } + } + + /// + /// Can be used to render the element with a specific Id for DOM selections. + /// + public string ElementId { get; set; } + + /// + /// The Display order of the group. + /// Default value: 1000. + /// + public int Order { get; set; } + + public ApplicationMenuGroup( + [NotNull] string name, + [NotNull] string displayName, + string elementId = null, + int order = DefaultOrder) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + Check.NotNullOrWhiteSpace(displayName, nameof(displayName)); + + Name = name; + DisplayName = displayName; + ElementId = elementId; + Order = order; + } + + private string GetDefaultElementId() + { + return "MenuGroup_" + Name; + } + + public override string ToString() + { + return $"[ApplicationMenuGroup] Name = {Name}"; + } +} diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroupList.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroupList.cs new file mode 100644 index 0000000000..f280f5d4f9 --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroupList.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Volo.Abp.UI.Navigation; + +public class ApplicationMenuGroupList: List +{ + public ApplicationMenuGroupList() + { + + } + + public ApplicationMenuGroupList(int capacity) + : base(capacity) + { + + } + + public ApplicationMenuGroupList(IEnumerable collection) + : base(collection) + { + + } + + public void Normalize() + { + Order(); + } + + private void Order() + { + var orderedItems = this.OrderBy(item => item.Order).ToArray(); + Clear(); + AddRange(orderedItems); + } +} diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs index 32241ba7ac..dc1614e3e5 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs @@ -92,6 +92,11 @@ public class ApplicationMenuItem : IHasMenuItems, IHasSimpleStateCheckers public string CssClass { get; set; } + /// + /// Can be used to group menu items. + /// + public string GroupName { get; set; } + public ApplicationMenuItem( [NotNull] string name, [NotNull] string displayName, @@ -101,6 +106,7 @@ public class ApplicationMenuItem : IHasMenuItems, IHasSimpleStateCheckers>(); Items = new ApplicationMenuItemList(); diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IHasMenuGroups.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IHasMenuGroups.cs new file mode 100644 index 0000000000..fbbe7a63a4 --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IHasMenuGroups.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.UI.Navigation; + +public interface IHasMenuGroups +{ + /// + /// Menu groups. + /// + ApplicationMenuGroupList Groups { get; } +} diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs index aa933c6e09..349aa8e5a5 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs @@ -96,6 +96,7 @@ public class MenuManager : IMenuManager, ITransientDependency } NormalizeMenu(menu); + NormalizeMenuGroup(menu); return menu; } @@ -159,4 +160,23 @@ public class MenuManager : IMenuManager, ITransientDependency menuWithItems.Items.Normalize(); } + + protected virtual void NormalizeMenuGroup(ApplicationMenu applicationMenu) + { + foreach (var menuGroup in applicationMenu.Items.Where(x => !x.GroupName.IsNullOrWhiteSpace()).GroupBy(x => x.GroupName)) + { + var group = applicationMenu.GetMenuGroupOrNull(menuGroup.First().GroupName); + if (group != null) + { + continue; + } + + foreach (var menuItem in menuGroup) + { + menuItem.GroupName = null; + } + } + + applicationMenu.Groups.Normalize(); + } } diff --git a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/AbpUnitOfWorkModule.cs b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/AbpUnitOfWorkModule.cs index 036e101397..541340183a 100644 --- a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/AbpUnitOfWorkModule.cs +++ b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/AbpUnitOfWorkModule.cs @@ -7,6 +7,6 @@ public class AbpUnitOfWorkModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services.OnRegistred(UnitOfWorkInterceptorRegistrar.RegisterIfNeeded); + context.Services.OnRegistered(UnitOfWorkInterceptorRegistrar.RegisterIfNeeded); } } diff --git a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs index cc29fd23a8..8713b2ec7b 100644 --- a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs +++ b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs @@ -193,7 +193,7 @@ public class UnitOfWork : IUnitOfWork, ITransientDependency if (_databaseApis.ContainsKey(key)) { - throw new AbpException("There is already a database API in this unit of work with given key: " + key); + throw new AbpException("There is already a database API in this unit of work with given key."); } _databaseApis.Add(key, api); @@ -221,7 +221,7 @@ public class UnitOfWork : IUnitOfWork, ITransientDependency if (_transactionApis.ContainsKey(key)) { - throw new AbpException("There is already a transaction API in this unit of work with given key: " + key); + throw new AbpException("There is already a transaction API in this unit of work with given key."); } _transactionApis.Add(key, api); diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs index 476bf2de06..321599c35c 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs @@ -16,7 +16,7 @@ public class AbpValidationModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services.OnRegistred(ValidationInterceptorRegistrar.RegisterIfNeeded); + context.Services.OnRegistered(ValidationInterceptorRegistrar.RegisterIfNeeded); AutoAddObjectValidationContributors(context.Services); } @@ -39,7 +39,7 @@ public class AbpValidationModule : AbpModule { var contributorTypes = new List(); - services.OnRegistred(context => + services.OnRegistered(context => { if (typeof(IObjectValidationContributor).IsAssignableFrom(context.ImplementationType)) { diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ar.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ar.json index 2cdb6999b5..252bee394f 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ar.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ar.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "يجب أن يكون الحقل {0} سلسلة أحرف طولها {1} كحد أدنى و {2} كحد أقصى.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "الحقل {0} ليس عنوانا URL صالحًا مؤهلاً بالكامل سواء كان عنوان http أو https أو ftp", "The field {0} is invalid.": "الحقل {0} غير صالح.", + "The value '{0}' is invalid.": "القيمة '{0}' غير صالحة.", + "The field {0} must be a number.": "يجب أن يكون الحقل {0} رقمًا.", + "The field must be a number.": "يجب أن يكون الحقل رقمًا.", "ThisFieldIsNotAValidCreditCardNumber.": "هذا الحقل لا يمثل رقم بطاقة ائتمان صالح.", "ThisFieldIsNotValid.": "هذا الحقل غير صالح.", "ThisFieldIsNotAValidEmailAddress.": "هذا الحقل لا يمثل عنوان بريد إلكتروني صالح.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json index dce07f8c91..60e2113906 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Pole {0} musí být řetězec o minimální délce {2} a maximální délce {1} znaků.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Pole {0} není platná plně kvalifikovaná adresa http, https, nebo ftp URL.", "The field {0} is invalid.": "Pole {0} je neplatné.", + "The value '{0}' is invalid.": "Hodnota '{0}' je neplatná.", + "The field {0} must be a number.": "Pole {0} musí být číslo.", + "The field must be a number.": "Pole musí být číslo.", "ThisFieldIsNotAValidCreditCardNumber.": "V poli {0} není platné číslo kreditní karty.", "ThisFieldIsNotValid.": "{0} není platný.", "ThisFieldIsNotAValidEmailAddress.": "V poli {0} není platný email.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/de.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/de.json index a72a44a3bb..209d8536ae 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/de.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/de.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Das Feld {0} muss eine Zeichenfolge mit einer Mindestlänge von {2} und einer Maximallänge von {1} sein.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Das Feld {0} ist keine gültige, vollqualifizierte http-, https- oder ftp-URL.", "The field {0} is invalid.": "Das Feld {0} ist ungültig.", + "The value '{0}' is invalid.": "Der Wert '{0}' ist ungültig.", + "The field {0} must be a number.": "Das Feld {0} muss eine Zahl sein.", + "The field must be a number.": "Das Feld muss eine Zahl sein.", "ThisFieldIsNotAValidCreditCardNumber.": "Dieses Feld ist keine gültige Kreditkartennummer.", "ThisFieldIsNotValid.": "Dieses Feld ist ungültig.", "ThisFieldIsNotAValidEmailAddress.": "Dieses Feld ist keine gültige E-Mail-Adresse.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/el.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/el.json index e2df0315f3..bed1964ac4 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/el.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/el.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Το πεδίο {0} πρέπει να είναι μια συμβολοσειρά με ελάχιστο μήκος {2} και μέγιστο μήκος {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Το πεδίο {0} δεν είναι έγκυρη πλήρως πιστοποιημένη διεύθυνση URL http, https ή ftp.", "The field {0} is invalid.": "Το πεδίο {0} δεν είναι έγκυρο.", + "The value '{0}' is invalid.": "Η τιμή '{0}' δεν είναι έγκυρη.", + "The field {0} must be a number.": "Το πεδίο {0} πρέπει να είναι αριθμός.", + "The field must be a number.": "Το πεδίο πρέπει να είναι αριθμός.", "ThisFieldIsNotAValidCreditCardNumber.": "Αυτό το πεδίο δεν περιέχει έγκυρο αριθμό πιστωτικής κάρτας.", "ThisFieldIsNotValid.": "Αυτό το πεδίο δεν είναι έγκυρο.", "ThisFieldIsNotAValidEmailAddress.": "Αυτό το πεδίο δεν περιέχει έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en-GB.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en-GB.json index a53b55a853..0874616561 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en-GB.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en-GB.json @@ -17,6 +17,9 @@ "The field {0} is invalid.": "The field {0} is invalid.", "ThisFieldIsNotAValidCreditCardNumber.": "This field is not a valid credit card number.", "ThisFieldIsNotValid.": "This field is not valid.", + "The value '{0}' is invalid.": "The value '{0}' is invalid.", + "The field {0} must be a number.": "The field {0} must be a number.", + "The field must be a number.": "The field must be a number.", "ThisFieldIsNotAValidEmailAddress.": "This field is not a valid e-mail address.", "ThisFieldOnlyAcceptsFilesWithTheFollowingExtensions:{0}": "This field only accepts files with the following extensions: {0}", "ThisFieldMustBeAStringOrArrayTypeWithAMaximumLengthOf{0}": "This field must be a string or array type with a maximum length of '{0}'.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en.json index 29035fb57f..8a866ef936 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "The {0} field is not a valid fully-qualified http, https, or ftp URL.", "The field {0} is invalid.": "The field {0} is invalid.", + "The value '{0}' is invalid.": "The value '{0}' is invalid.", + "The field {0} must be a number.": "The field {0} must be a number.", + "The field must be a number.": "The field must be a number.", "ThisFieldIsNotAValidCreditCardNumber.": "This field is not a valid credit card number.", "ThisFieldIsNotValid.": "This field is not valid.", "ThisFieldIsNotAValidEmailAddress.": "This field is not a valid e-mail address.", @@ -33,4 +36,4 @@ "ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "This field is not a valid fully-qualified http, https, or ftp URL.", "ThisFieldIsInvalid.": "This field is invalid." } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/es.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/es.json index 55884667f6..969cb3f3a9 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/es.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/es.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "El campo {0} debe ser una cadena con una longitud mínima de {2} y máxima de {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "El campo {0} no es una URL (http, https o ftp) valida.", "The field {0} is invalid.": "El campo {0} no es valido.", + "The value '{0}' is invalid.": "El valor '{0}' no es válido.", + "The field {0} must be a number.": "El campo {0} debe ser un número.", + "The field must be a number.": "El campo debe ser un número.", "ThisFieldIsNotAValidCreditCardNumber.": "El campo no es un número de tarjeta de crédito valido.", "ThisFieldIsNotValid.": "Este campo no es valido.", "ThisFieldIsNotAValidEmailAddress.": "Este campo no es una dirección de e-mail valida.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fa.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fa.json index bdec7afe06..39ec042dcb 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fa.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fa.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "فیلد {0} باید رشته ای با حداقل طول {2} و حداکثر باشد طول {1}. ", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "فیلد {0} یک نشانی اینترنتی http ، https ، یا ftp کاملاً واجد شرایط نیست.", "The field {0} is invalid.": "فیلد {0} نامعتبر است.", + "The value '{0}' is invalid.": "مقدار '{0}' نامعتبر است.", + "The field {0} must be a number.": "فیلد {0} باید یک عدد باشد.", + "The field must be a number.": "فیلد باید یک عدد باشد.", "ThisFieldIsNotAValidCreditCardNumber.": "این قسمت شماره کارت اعتباری معتبری نیست.", "ThisFieldIsNotValid.": "این قسمت معتبر نیست.", "ThisFieldIsNotAValidEmailAddress.": "این قسمت آدرس ایمیل معتبری نیست.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fi.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fi.json index f4501b87cf..e73fc5670b 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fi.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fi.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Kentän {0} on oltava merkkijono, jonka vähimmäispituus on {2} ja enimmäispituus {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "{0} -kenttä ei ole kelvollinen täysin hyväksytty http, https tai ftp URL.", "The field {0} is invalid.": "Kenttä {0} on virheellinen.", + "The value '{0}' is invalid.": "Arvo '{0}' on virheellinen.", + "The field {0} must be a number.": "Kentän {0} on oltava numero.", + "The field must be a number.": "Kentän on oltava numero.", "ThisFieldIsNotAValidCreditCardNumber.": "Tämä kenttä ei ole kelvollinen luottokortin numero.", "ThisFieldIsNotValid.": "Tämä kenttä ei kelpaa.", "ThisFieldIsNotAValidEmailAddress.": "Tämä kenttä ei ole kelvollinen sähköpostiosoite.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fr.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fr.json index 558bc9ca11..0adf748127 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fr.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/fr.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Le champ {0} doit être une chaîne d'une longueur minimale de {2} et d'une longueur maximale de {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Le champ {0} n'est pas une URL http, https ou ftp complète valide.", "The field {0} is invalid.": "Le champ {0} n'est pas valide.", + "The value '{0}' is invalid.": "La valeur '{0}' n'est pas valide.", + "The field {0} must be a number.": "Le champ {0} doit être un nombre.", + "The field must be a number.": "Le champ doit être un nombre.", "ThisFieldIsNotAValidCreditCardNumber.": "Ce champ n'est pas un numéro de carte de crédit valide.", "ThisFieldIsNotValid.": "Ce champ n'est pas valide.", "ThisFieldIsNotAValidEmailAddress.": "Ce champ n'est pas une adresse e-mail valide.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hi.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hi.json index fab1a394b4..d7ae279c18 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hi.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hi.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "फ़ील्ड {0} की लंबाई न्यूनतम {2} और अधिकतम लंबाई {1} होनी चाहिए।", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "{0} फ़ील्ड मान्य पूर्णत: योग्य http, https, या ftp URL नहीं है।", "The field {0} is invalid.": "फ़ील्ड {0} अमान्य है।", + "The value '{0}' is invalid.": "मान '{0}' अमान्य है।", + "The field {0} must be a number.": "फ़ील्ड {0} एक संख्या होनी चाहिए।", + "The field must be a number.": "फ़ील्ड एक संख्या होनी चाहिए।", "ThisFieldIsNotAValidCreditCardNumber.": "यह फ़ील्ड मान्य क्रेडिट कार्ड नंबर नहीं है।", "ThisFieldIsNotValid.": "यह फ़ील्ड मान्य नहीं है।", "ThisFieldIsNotAValidEmailAddress.": "यह फ़ील्ड मान्य ई-मेल पता नहीं है।", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hr.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hr.json index 6bb5cf7d5e..dcc01e9b0a 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hr.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hr.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Polje {0} mora biti text sa minimalnom du�inom od {2} i maksimalnom dužinom od {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "{0} nije valjan potpuno kvalificirani http, https, ili ftp URL.", "The field {0} is invalid.": "Polje {0} nije važeće.", + "The value '{0}' is invalid.": "Vrijednost '{0}' nije važeća.", + "The field {0} must be a number.": "Polje {0} mora biti broj.", + "The field must be a number.": "Polje mora biti broj.", "ThisFieldIsNotAValidCreditCardNumber.": "Ovo polje nije važeći broj kreditne kartice.", "ThisFieldIsNotValid.": "Ovo polje nije valjano.", "ThisFieldIsNotAValidEmailAddress.": "Ovo polje nije valjana e-mail adresa.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hu.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hu.json index 3db0f6f5c0..b06d9d3c47 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hu.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/hu.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "A {0} mezőnek szöveget kell tartalmaznia minimum {2} és maximum {1} hosszan.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "A {0} mező nem érvényes fully-qualified http, https, vagy ftp URL cím.", "The field {0} is invalid.": "A {0} mező nem érvényes.", + "The value '{0}' is invalid.": "A(z) '{0}' érték érvénytelen.", + "The field {0} must be a number.": "A(z) {0} mezőnek számnak kell lennie.", + "The field must be a number.": "A mezőnek számnak kell lennie.", "ThisFieldIsNotAValidCreditCardNumber.": "A mező nem érvényes bankkártya számot tartalmaz.", "ThisFieldIsNotValid.": "A mező nem érvényes.", "ThisFieldIsNotAValidEmailAddress.": "A mező nem érvényes email cím.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/is.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/is.json index 8f3ef3ae88..1f9c858d2a 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/is.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/is.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Reiturinn {0} verður að vera strengur með lágmarkslengd {2} og hámarkslengd {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Reiturinn {0} er ekki fullgild http, https, eða ftp slóð.", "The field {0} is invalid.": "Reiturinn {0} er ekki rétt útfylltur.", + "The value '{0}' is invalid.": "Gildið '{0}' er ógilt.", + "The field {0} must be a number.": "Reiturinn {0} verður að vera númer.", + "The field must be a number.": "Reiturinn verður að vera númer.", "ThisFieldIsNotAValidCreditCardNumber.": "Þessi reitur hefur ekki gilt kreditkortanúmer.", "ThisFieldIsNotValid.": "Reiturinn er ekki rétt útfylltur.", "ThisFieldIsNotAValidEmailAddress.": "Þessi reitur hefur ekki gilt netfang.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/it.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/it.json index bad68c117f..4cf02600bc 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/it.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/it.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Il campo {0} deve essere una stringa con una lunghezza minima di {2} e una lunghezza massima di {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Il campo {0} non è un URL http, https o ftp completo e valido.", "The field {0} is invalid.": "Il campo {0} non è valido.", + "The value '{0}' is invalid.": "Il valore '{0}' non è valido.", + "The field {0} must be a number.": "Il campo {0} deve essere un numero.", + "The field must be a number.": "Il campo deve essere un numero.", "ThisFieldIsNotAValidCreditCardNumber.": "Questo campo non è un numero di carta di credito valido.", "ThisFieldIsNotValid.": "Questo campo non è valido.", "ThisFieldIsNotAValidEmailAddress.": "Questo campo non è un indirizzo e-mail valido.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/nl.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/nl.json index ff0edfa54d..096d2fcbc3 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/nl.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/nl.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Het veld {0} moet een tekenreeks zijn met een minimale lengte van {2} en een maximale lengte van {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Het veld {0} is geen geldige, volledig gekwalificeerde http-, https- of ftp-URL.", "The field {0} is invalid.": "Het veld {0} is ongeldig.", + "The value '{0}' is invalid.": "De waarde '{0}' is ongeldig.", + "The field {0} must be a number.": "Het veld {0} moet een getal zijn.", + "The field must be a number.": "Het veld moet een getal zijn.", "ThisFieldIsNotAValidCreditCardNumber.": "Dit veld is geen geldig krediet kaartnummer.", "ThisFieldIsNotValid.": "Dir veld is ongeldig.", "ThisFieldIsNotAValidEmailAddress.": "Dit veld is geen geldig e-mail adres.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl-PL.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl-PL.json index c4a6cfe9d6..29baf6efff 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl-PL.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl-PL.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Pole {0} musi być łańcuchem znaków o minimalnej długości {2} i maksymalnej długości {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Pole {0} nie jest prawidłowym, w pełni kwalifikowanym adresem URL http, https lub ftp.", "The field {0} is invalid.": "Pole {0} jest niepoprawne.", + "The value '{0}' is invalid.": "Wartość '{0}' jest nieprawidłowa.", + "The field {0} must be a number.": "Pole {0} musi być liczbą.", + "The field must be a number.": "Pole musi być liczbą.", "ThisFieldIsNotAValidCreditCardNumber.": "To pole nie jest prawidłowym numerem karty kredytowej.", "ThisFieldIsNotValid.": "To pole jest nieprawidłowe.", "ThisFieldIsNotAValidEmailAddress.": "To pole nie jest prawidłowym adresem e-mail.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pt-BR.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pt-BR.json index 1168a19317..a0bec74d8c 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pt-BR.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pt-BR.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "O campo {0} deve ser uma palavra com o tamanho mínimo de {2} e tamanho máximo de {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "O campo {0} não é um http, https, ou FTP válido.", "The field {0} is invalid.": "O campo {0} é inválido.", + "The value '{0}' is invalid.": "O valor '{0}' é inválido.", + "The field {0} must be a number.": "O campo {0} deve ser um número.", + "The field must be a number.": "O campo deve ser um número.", "ThisFieldIsNotAValidCreditCardNumber.": "Não é um cartão de crédito válido.", "ThisFieldIsNotValid.": "Campo inválido.", "ThisFieldIsNotAValidEmailAddress.": "E-mail inválido.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ro-RO.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ro-RO.json index ce03ecb52b..5fe4e459a2 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ro-RO.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ro-RO.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Câmpul {0} trebuie să fie un string cu lungimea minimă de {2} şi lungimea maximă de {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Câmpul {0} nu este o adresă validă complet http, https sau ftp.", "The field {0} is invalid.": "Câmpul {0} este invalid.", + "The value '{0}' is invalid.": "Valoarea '{0}' este nevalidă.", + "The field {0} must be a number.": "Câmpul {0} trebuie să fie un număr.", + "The field must be a number.": "Câmpul trebuie să fie un număr.", "ThisFieldIsNotAValidCreditCardNumber.": "Acest câmp nu este un număr de card de credit valid.", "ThisFieldIsNotValid.": "Acest câmp nu este valid.", "ThisFieldIsNotAValidEmailAddress.": "Acest câmp nu este o adresă de e-mail validă.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ru.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ru.json index 877a3bb1dc..a52c619933 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ru.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ru.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Поле {0} должно быть строкой с минимальной длиной {2} и максимальной длиной {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Поле {0} не является действительным полным http, https или ftp адресом.", "The field {0} is invalid.": "Значение в поле {0} недопустимо.", + "The value '{0}' is invalid.": "Значение '{0}' недопустимо.", + "The field {0} must be a number.": "Поле {0} должно быть числом.", + "The field must be a number.": "Поле должно быть числом.", "ThisFieldIsNotAValidCreditCardNumber.": "Это поле не содержит действительный номер кредитной карты.", "ThisFieldIsNotValid.": "Значение в этом поле недействительно.", "ThisFieldIsNotAValidEmailAddress.": "Это поле не содержит действительный адрес электронной почты.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/sk.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/sk.json index d5f310aa66..856124c31f 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/sk.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/sk.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Pole {0} musí byť reťazec s minimálnou dĺžkou {2} a maximálnou dĺžkou {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "V poli {0} nie je platná plne kvalifikovaná adresa http, https alebo ftp URL.", "The field {0} is invalid.": "Pole {0} je neplatné.", + "The value '{0}' is invalid.": "Hodnota '{0}' je neplatná.", + "The field {0} must be a number.": "Pole {0} musí byť číslo.", + "The field must be a number.": "Pole musí byť číslo.", "ThisFieldIsNotAValidCreditCardNumber.": "V tomto poli nie je platné číslo kreditnej karty.", "ThisFieldIsNotValid.": "Toto pole nie je platné.", "ThisFieldIsNotAValidEmailAddress.": "V tomto poli nie je platná e-mailová adresa.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/sl.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/sl.json index 374425728d..0eaa31916b 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/sl.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/sl.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Polje {0} mora biti niz z najmanjšo dolžino {2} in največjo dolžino {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Polje {0} ni veljaven popolnoma kvalificiran URL http, https ali ftp.", "The field {0} is invalid.": "Polje {0} ni veljavno.", + "The value '{0}' is invalid.": "Vrednost '{0}' ni veljavna.", + "The field {0} must be a number.": "Polje {0} mora biti številka.", + "The field must be a number.": "Polje mora biti številka.", "ThisFieldIsNotAValidCreditCardNumber.": "To polje ni veljavna številka kreditne kartice.", "ThisFieldIsNotValid.": "To polje ni veljavno.", "ThisFieldIsNotAValidEmailAddress.": "To polje ni veljaven e-poštni naslov.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/tr.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/tr.json index 9d784c57d9..cb30adde0d 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/tr.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/tr.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "{0} alanı en az {2}, en fazla {1} uzunluğunda bir metin olmalıdır.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "{0} alanı geçerli bir http, https, ya da ftp adresi olmalıdır.", "The field {0} is invalid.": "{0} alanı geçerli değil.", + "The value '{0}' is invalid.": "{0} değeri geçerli değil.", + "The field {0} must be a number.": "{0} alanı bir sayı olmalıdır.", + "The field must be a number.": "Bu alan bir sayı olmalıdır.", "ThisFieldIsNotAValidCreditCardNumber.": "Bu alan geçerli bir kredi kartı numarası olmalıdır.", "ThisFieldIsNotValid.": "Bu alan geçerli değil.", "ThisFieldIsNotAValidEmailAddress.": "Bu alan geçerli bir e-posta adresi olmalıdır.", @@ -33,4 +36,4 @@ "ThisFieldMustBeGreaterThanOrEqual{0}": "Bu alan {0}'dan büyük veya eşit olmalıdır.", "ThisFieldMustBeLessOrEqual{0}": "Bu alan {0}'dan küçük veya eşit olmalıdır." } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/vi.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/vi.json index 9bfc2c10ca..984f5c2e2b 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/vi.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/vi.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Trường {0} phải là một chuỗi với độ dài tối thiểu {2} và tối đa là {1}.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Trường {0} không phải là một http, https, hoặc ftp URL đủ điều kiện hợp lệ.", "The field {0} is invalid.": "Trường {0} không có hiệu lực", + "The value '{0}' is invalid.": "Giá trị '{0}' không hợp lệ.", + "The field {0} must be a number.": "Trường {0} phải là một số.", + "The field must be a number.": "Trường phải là một số.", "ThisFieldIsNotAValidCreditCardNumber.": "Trường này không phải là số thẻ tín dụng hợp lệ.", "ThisFieldIsNotValid.": "Trường này không hợp lệ.", "ThisFieldIsNotAValidEmailAddress.": "Trường này không phải là địa chỉ e-mail hợp lệ.", diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json index 3c1a566112..262b2e08fa 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "字段{0}必须是最小长度为{2}并且最大长度{1}的字符串.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "字段{0}不是有效的完全限定的http,https或ftp URL.", "The field {0} is invalid.": "字段{0}是无效值.", + "The value '{0}' is invalid.": "'{0}'是无效值", + "The field {0} must be a number.": "字段{0}必须是数字.", + "The field must be a number.": "该字段必须是数字.", "ThisFieldIsNotAValidCreditCardNumber.": "字段不是有效的信用卡号码.", "ThisFieldIsNotValid.": "验证未通过.", "ThisFieldIsNotAValidEmailAddress.": "字段不是有效的邮箱地址.", @@ -33,4 +36,4 @@ "ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "字段{0}不是有效的完全限定的http,https或ftp URL.", "ThisFieldIsInvalid.": "该字段无效." } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hant.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hant.json index ec0fabd121..0f14ca2a40 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hant.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hant.json @@ -16,6 +16,9 @@ "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "欄位{0}必須是最小長度為{2}並且最大長度{1}的字串.", "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "欄位{0}不是有效的完全限定的http,https或ftp URL.", "The field {0} is invalid.": "此欄位{0}是無效值.", + "The value '{0}' is invalid.": "'{0}'是無效值", + "The field {0} must be a number.": "欄位{0}必須是號碼.", + "The field must be a number.": "此欄位{0}必須是號碼.", "ThisFieldIsNotAValidCreditCardNumber.": "此欄位不是有效的信用卡號碼.", "ThisFieldIsNotValid.": "此驗證未通過.", "ThisFieldIsNotAValidEmailAddress.": "此欄位不是有效的郵箱地址.", diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs index b41ea093ff..7d5d4d0561 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs @@ -14,7 +14,7 @@ public class AbpAuthorizationTestModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services.OnRegistred(onServiceRegistredContext => + context.Services.OnRegistered(onServiceRegistredContext => { if (typeof(IMyAuthorizedService1).IsAssignableFrom(onServiceRegistredContext.ImplementationType) && !DynamicProxyIgnoreTypes.Contains(onServiceRegistredContext.ImplementationType)) diff --git a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobExecuter_Tests.cs b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobExecuter_Tests.cs index 42fe0da502..db7b5f5cc8 100644 --- a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobExecuter_Tests.cs +++ b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobExecuter_Tests.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using Shouldly; using Xunit; @@ -59,7 +60,7 @@ public class BackgroundJobExecuter_Tests : BackgroundJobsTestBase jobObject.ExecutedValues.ShouldContain("42"); } - + [Fact] public async Task Should_Change_TenantId_If_EventData_Is_MultiTenant() { @@ -77,7 +78,7 @@ public class BackgroundJobExecuter_Tests : BackgroundJobsTestBase new MyJobArgs("42", tenantId) ) ); - + await _backgroundJobExecuter.ExecuteAsync( new JobExecutionContext( ServiceProvider, @@ -91,4 +92,48 @@ public class BackgroundJobExecuter_Tests : BackgroundJobsTestBase jobObject.TenantId.ShouldBe(tenantId); asyncJobObject.TenantId.ShouldBe(tenantId); } + + [Fact] + public async Task Should_Cancel_Job() + { + //Arrange + var cts = new CancellationTokenSource(); + cts.Cancel(); + + var jobObject = GetRequiredService(); + jobObject.ExecutedValues.ShouldBeEmpty(); + + //Act + await _backgroundJobExecuter.ExecuteAsync( + new JobExecutionContext( + ServiceProvider, + typeof(MyJob), + new MyJobArgs("42"), + cts.Token + ) + ); + + //Assert + jobObject.Canceled.ShouldBeTrue(); + + //Arrange + var asyncCts = new CancellationTokenSource(); + asyncCts.Cancel(); + + var asyncJobObject = GetRequiredService(); + asyncJobObject.ExecutedValues.ShouldBeEmpty(); + + //Act + await _backgroundJobExecuter.ExecuteAsync( + new JobExecutionContext( + ServiceProvider, + typeof(MyAsyncJob), + new MyAsyncJobArgs("42"), + asyncCts.Token + ) + ); + + //Assert + asyncJobObject.Canceled.ShouldBeTrue(); + } } diff --git a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/MyAsyncJob.cs b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/MyAsyncJob.cs index 79c5ea887a..d738b36130 100644 --- a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/MyAsyncJob.cs +++ b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/MyAsyncJob.cs @@ -1,26 +1,39 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; namespace Volo.Abp.BackgroundJobs; public class MyAsyncJob : AsyncBackgroundJob, ISingletonDependency { public List ExecutedValues { get; } = new List(); - + public Guid? TenantId { get; set; } - + private readonly ICurrentTenant _currentTenant; + private readonly ICancellationTokenProvider _cancellationTokenProvider; - public MyAsyncJob(ICurrentTenant currentTenant) + public bool Canceled { get; set; } + + public MyAsyncJob( + ICurrentTenant currentTenant, + ICancellationTokenProvider cancellationTokenProvider) { _currentTenant = currentTenant; + _cancellationTokenProvider = cancellationTokenProvider; } public override Task ExecuteAsync(MyAsyncJobArgs args) { + if (_cancellationTokenProvider.Token.IsCancellationRequested) + { + Canceled = true; + } + ExecutedValues.Add(args.Value); TenantId = _currentTenant.Id; return Task.CompletedTask; diff --git a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/MyJob.cs b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/MyJob.cs index 3b01ec05d1..1f9658162d 100644 --- a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/MyJob.cs +++ b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/MyJob.cs @@ -1,25 +1,38 @@ using System; using System.Collections.Generic; +using System.Threading; using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; namespace Volo.Abp.BackgroundJobs; public class MyJob : BackgroundJob, ISingletonDependency { public List ExecutedValues { get; } = new List(); - + public Guid? TenantId { get; set; } private readonly ICurrentTenant _currentTenant; - - public MyJob(ICurrentTenant currentTenant) + private readonly ICancellationTokenProvider _cancellationTokenProvider; + + public bool Canceled { get; set; } + + public MyJob( + ICurrentTenant currentTenant, + ICancellationTokenProvider cancellationTokenProvider) { _currentTenant = currentTenant; + _cancellationTokenProvider = cancellationTokenProvider; } public override void Execute(MyJobArgs args) { + if (_cancellationTokenProvider.Token.IsCancellationRequested) + { + Canceled = true; + } + ExecutedValues.Add(args.Value); TenantId = _currentTenant.Id; } diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/AbpInterceptionTestBase.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/AbpInterceptionTestBase.cs index f4b584701f..e244237598 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/AbpInterceptionTestBase.cs +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DynamicProxy/AbpInterceptionTestBase.cs @@ -19,7 +19,7 @@ public abstract class AbpInterceptionTestBase : AbpAsyncIntegrat services.AddTransient(); services.AddTransient(); - services.OnRegistred(registration => + services.OnRegistered(registration => { if (typeof(SimpleInterceptionTargetClass) == registration.ImplementationType) { diff --git a/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs b/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs index 276be829b2..37e6b07b8f 100644 --- a/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs +++ b/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs @@ -109,7 +109,7 @@ public class ApplicationService_FluentValidation_Tests : AbpIntegratedTest + context.Services.OnRegistered(onServiceRegistredContext => { if (typeof(IMyAppService).IsAssignableFrom(onServiceRegistredContext.ImplementationType) && !DynamicProxyIgnoreTypes.Contains(onServiceRegistredContext.ImplementationType)) diff --git a/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo.Abp.MultiLingualObjects.Tests.csproj b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo.Abp.MultiLingualObjects.Tests.csproj index a037cfbcf3..b26ea500bf 100644 --- a/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo.Abp.MultiLingualObjects.Tests.csproj +++ b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo.Abp.MultiLingualObjects.Tests.csproj @@ -1,18 +1,21 @@ - + - - net7.0 - - + + net7.0 + + enable + - - - - - - - + + + + + + + + + diff --git a/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/AbpMultiLingualObjectsTestModule.cs b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/AbpMultiLingualObjectsTestModule.cs index f099b4fa6c..6fc3a45132 100644 --- a/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/AbpMultiLingualObjectsTestModule.cs +++ b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/AbpMultiLingualObjectsTestModule.cs @@ -1,19 +1,34 @@ -using Volo.Abp.Autofac; -using Volo.Abp.Localization; -using Volo.Abp.Modularity; -using Volo.Abp.ObjectMapping; -using Volo.Abp.Settings; - -namespace Volo.Abp.MultiLingualObjects; - -[DependsOn( - typeof(AbpAutofacModule), - typeof(AbpLocalizationModule), - typeof(AbpSettingsModule), - typeof(AbpObjectMappingModule), - typeof(AbpMultiLingualObjectsModule), - typeof(AbpTestBaseModule) -)] -public class AbpMultiLingualObjectsTestModule : AbpModule -{ -} +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Autofac; +using Volo.Abp.AutoMapper; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.ObjectMapping; +using Volo.Abp.Settings; + +namespace Volo.Abp.MultiLingualObjects; + +[DependsOn( + typeof(AbpAutofacModule), + typeof(AbpLocalizationModule), + typeof(AbpSettingsModule), + typeof(AbpObjectMappingModule), + typeof(AbpMultiLingualObjectsModule), + typeof(AbpTestBaseModule), + typeof(AbpAutoMapperModule) +)] +public class AbpMultiLingualObjectsTestModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.DefinitionProviders.Add(); + }); + context.Services.AddAutoMapperObjectMapper(); + Configure(options => + { + options.AddMaps(validate: true); + }); + } +} diff --git a/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/MultiLingualObjectManager_Tests.cs b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/MultiLingualObjectManager_Tests.cs index afe15bff99..e442d80953 100644 --- a/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/MultiLingualObjectManager_Tests.cs +++ b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/MultiLingualObjectManager_Tests.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Shouldly; +using Volo.Abp.AutoMapper; using Volo.Abp.Localization; using Volo.Abp.MultiLingualObjects.TestObjects; using Volo.Abp.Testing; @@ -14,40 +16,63 @@ public class MultiLingualObjectManager_Tests : AbpIntegratedTest _books; + private readonly IMapperAccessor _mapperAccessor; + private readonly Dictionary _testTranslations = new() + { + ["ar"] = "C# التعمق في", + ["zh-Hans"] = "深入理解C#", + ["en"] = "C# in Depth" + }; + public MultiLingualObjectManager_Tests() { - _multiLingualObjectManager = ServiceProvider.GetRequiredService(); - + _multiLingualObjectManager = ServiceProvider.GetRequiredService(); + + //Single Lookup + _book = GetTestBook("en", "zh-Hans"); + //Bulk lookup + _books = new List + { + //has no translations + GetTestBook(), + //english only + GetTestBook("en"), + //arabic only + GetTestBook("ar"), + //arabic + english + GetTestBook("en","ar"), + //arabic + english + chineese + GetTestBook("en", "ar", "zh-Hans") + }; + _mapperAccessor = ServiceProvider.GetRequiredService(); + } + MultiLingualBook GetTestBook(params string[] included) + { var id = Guid.NewGuid(); - _book = new MultiLingualBook(id, 100) - { - Translations = new List() - }; - - var en = new MultiLingualBookTranslation - { - Language = "en", - Name = "C# in Depth", - }; - var zh = new MultiLingualBookTranslation - { - Language = "zh-Hans", - Name = "深入理解C#", - }; - - _book.Translations.Add(en); - _book.Translations.Add(zh); - } + //Single book + var res = new MultiLingualBook(id, 100); + foreach (var language in included) + { + res.Translations.Add(new MultiLingualBookTranslation + { + Language = language, + Name = _testTranslations[language], + }); + } + + return res; + } + [Fact] public async Task GetTranslationAsync() { using (CultureHelper.Use("en-us")) { var translation = await _multiLingualObjectManager.GetTranslationAsync(_book); - - translation.Name.ShouldBe("C# in Depth"); + translation.ShouldNotBeNull(); + translation.Name.ShouldBe(_testTranslations["en"]); } } @@ -57,8 +82,8 @@ public class MultiLingualObjectManager_Tests : AbpIntegratedTest(_book, culture: "en"); + translation.ShouldNotBeNull(); + translation.Name.ShouldBe(_testTranslations["en"]); + } + } + + + [Fact] + public async Task GetBulkTranslationsAsync() + { + using (CultureHelper.Use("en-us")) + { + var translations = await _multiLingualObjectManager.GetBulkTranslationsAsync(_books); + foreach (var (entity, translation) in translations) + { + if (entity.Translations.Any(x => x.Language == "en")) + { + translation.ShouldNotBeNull(); + translation.Name.ShouldBe(_testTranslations["en"]); + } + else + { + translation.ShouldBeNull(); + } + } + } + } - translation.Name.ShouldBe("C# in Depth"); + [Fact] + public async Task GetBulkTranslationsFromListAsync() + { + using (CultureHelper.Use("en-us")) + { + var translations = await _multiLingualObjectManager.GetBulkTranslationsAsync(_books.Select(x => x.Translations)); + foreach (var translation in translations) + { + translation?.Name.ShouldBe(_testTranslations["en"]); + } } } + + [Fact] + public async Task TestBulkMapping() + { + using (CultureHelper.Use("en-us")) + { + var translations = await _multiLingualObjectManager.GetBulkTranslationsAsync(_books); + var translationsDict = translations.ToDictionary(x => x.entity.Id, x => x.translation); + var mapped = _mapperAccessor.Mapper.Map, List>(_books, options => + { + options.Items.Add(nameof(MultiLingualBookTranslation), translationsDict); + }); + Assert.Equal(mapped.Count, _books.Count); + for (int i = 0; i < mapped.Count; i++) + { + var og = _books[i]; + var m = mapped[i]; + Assert.Equal(og.Translations.FirstOrDefault(x => x.Language == "en")?.Name, m.Name); + } + } + } } diff --git a/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/MultiLingualObjectTestProfile.cs b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/MultiLingualObjectTestProfile.cs new file mode 100644 index 0000000000..89cc9a51b7 --- /dev/null +++ b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/MultiLingualObjectTestProfile.cs @@ -0,0 +1,23 @@ +namespace Volo.Abp.MultiLingualObjects; + +using System; +using System.Collections.Generic; +using global::AutoMapper; +using Volo.Abp.MultiLingualObjects.TestObjects; + +public class MultiLingualObjectTestProfile : Profile +{ + public MultiLingualObjectTestProfile() + { + CreateMap() + .ForMember(x => x.Name, + x => x.MapFrom((src, target, member, context) => + { + if (context.Items.TryGetValue(nameof(MultiLingualBookTranslation), out var translationsRaw) && translationsRaw is IReadOnlyDictionary translations) + { + return translations.GetValueOrDefault(src.Id)?.Name; + } + return null; + })); + } +} diff --git a/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/TestObjects/MultiLingualBook.cs b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/TestObjects/MultiLingualBook.cs index 92a295e0c5..3b880f48f2 100644 --- a/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/TestObjects/MultiLingualBook.cs +++ b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/TestObjects/MultiLingualBook.cs @@ -1,19 +1,19 @@ -using System; -using System.Collections.Generic; - -namespace Volo.Abp.MultiLingualObjects.TestObjects; - -public class MultiLingualBook : IMultiLingualObject -{ - public MultiLingualBook(Guid id, decimal price) - { - Id = id; - Price = price; - } - - public Guid Id { get; } - - public decimal Price { get; set; } - - public ICollection Translations { get; set; } -} +using System; +using System.Collections.Generic; + +namespace Volo.Abp.MultiLingualObjects.TestObjects; + +public class MultiLingualBook : IMultiLingualObject +{ + public MultiLingualBook(Guid id, decimal price) + { + Id = id; + Price = price; + } + + public Guid Id { get; } + + public decimal Price { get; set; } + + public ICollection Translations { get; set; } = new List(); +} diff --git a/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/TestObjects/MultiLingualBookDto.cs b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/TestObjects/MultiLingualBookDto.cs index ea7e7e9afd..8d61449961 100644 --- a/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/TestObjects/MultiLingualBookDto.cs +++ b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/TestObjects/MultiLingualBookDto.cs @@ -1,12 +1,12 @@ -using System; - -namespace Volo.Abp.MultiLingualObjects.TestObjects; - -public class MultiLingualBookDto -{ - public Guid Id { get; set; } - - public string Name { get; set; } - - public decimal Price { get; set; } -} +using System; + +namespace Volo.Abp.MultiLingualObjects.TestObjects; + +public class MultiLingualBookDto +{ + public Guid Id { get; set; } + + public string? Name { get; set; } + + public decimal Price { get; set; } +} diff --git a/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/TestObjects/MultiLingualBookTranslation.cs b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/TestObjects/MultiLingualBookTranslation.cs index 06cfc33b55..adf482fa17 100644 --- a/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/TestObjects/MultiLingualBookTranslation.cs +++ b/framework/test/Volo.Abp.MultiLingualObjects.Tests/Volo/Abp/MultiLingualObjects/TestObjects/MultiLingualBookTranslation.cs @@ -1,8 +1,8 @@ -namespace Volo.Abp.MultiLingualObjects.TestObjects; - -public class MultiLingualBookTranslation : IObjectTranslation -{ - public string Name { get; set; } - - public string Language { get; set; } -} +namespace Volo.Abp.MultiLingualObjects.TestObjects; + +public class MultiLingualBookTranslation : IObjectTranslation +{ + public string? Name { get; set; } + + public required string Language { get; set; } +} diff --git a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/AbpUiNavigationTestModule.cs b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/AbpUiNavigationTestModule.cs index 6088984cb9..c482963d75 100644 --- a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/AbpUiNavigationTestModule.cs +++ b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/AbpUiNavigationTestModule.cs @@ -16,6 +16,7 @@ public class AbpUiNavigationTestModule : AbpModule options.MenuContributors.Add(new MenuManager_Tests.TestMenuContributor1()); options.MenuContributors.Add(new MenuManager_Tests.TestMenuContributor2()); options.MenuContributors.Add(new MenuManager_Tests.TestMenuContributor3()); + options.MenuContributors.Add(new MenuManager_Tests.TestMenuContributor4()); options.MainMenuNames.Add(MenuManager_Tests.TestMenuContributor3.MenuName); }); diff --git a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs index b27de60c10..421e072b0f 100644 --- a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs +++ b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs @@ -46,7 +46,7 @@ public class MenuManager_Tests : AbpIntegratedTest mainMenu.Name.ShouldBe(StandardMenus.Main); mainMenu.DisplayName.ShouldBe("Main Menu"); - mainMenu.Items.Count.ShouldBe(2); + mainMenu.Items.Count.ShouldBe(5); mainMenu.Items[0].Name.ShouldBe("Dashboard"); mainMenu.Items[1].Name.ShouldBe(DefaultMenuNames.Application.Main.Administration); mainMenu.Items[1].Items[0].Name.ShouldBe("Administration.UserManagement"); @@ -63,12 +63,29 @@ public class MenuManager_Tests : AbpIntegratedTest mainMenu.Name.ShouldBe(StandardMenus.Main); - mainMenu.Items.Count.ShouldBe(3); + mainMenu.Items.Count.ShouldBe(6); mainMenu.Items.ShouldContain(x => x.Name == "Products"); mainMenu.Items.ShouldContain(x => x.Name == "Dashboard"); } + [Fact] + public async Task GetMainMenuAsync_GroupMenuItems() + { + var mainMenu = await _menuManager.GetMainMenuAsync(); + + mainMenu.Name.ShouldBe(StandardMenus.Main); + mainMenu.Items.Count.ShouldBe(6); + + mainMenu.Items[2].GroupName.ShouldBe("Layouts"); + mainMenu.Items[3].GroupName.ShouldBe("Layouts"); + mainMenu.Items[4].GroupName.ShouldBe(null); // No group defined + + var layoutsGroup = mainMenu.GetMenuGroup("Layouts"); + layoutsGroup.Name.ShouldBe("Layouts"); + layoutsGroup.DisplayName.ShouldBe("Layouts"); + } + /* Adds menu items: * - Administration * - User Management @@ -149,4 +166,29 @@ public class MenuManager_Tests : AbpIntegratedTest return Task.CompletedTask; } } + + /* Adds group and menu items: + * - Layouts + * - Toolbars + * - Page Header + */ + public class TestMenuContributor4 : IMenuContributor + { + public Task ConfigureMenuAsync(MenuConfigurationContext context) + { + if (context.Menu.Name != StandardMenus.Main) + { + return Task.CompletedTask; + } + + context.Menu.AddGroup(new ApplicationMenuGroup("Layouts", "Layouts")); + + context.Menu.AddItem(new ApplicationMenuItem("Toolbars", "Toolbars", url: "/layouts/toolbars", groupName: "Layouts")); + context.Menu.AddItem(new ApplicationMenuItem("PageHeader", "Page Header", url: "/layouts/page-header", groupName: "Layouts")); + + context.Menu.AddItem(new ApplicationMenuItem("Branding", "Branding", url: "/layouts/branding", groupName: "NotDefinedGroup")); + + return Task.CompletedTask; + } + } } diff --git a/framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs b/framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs index 535e9fb613..f58b000af4 100644 --- a/framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs +++ b/framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs @@ -201,7 +201,7 @@ public class ApplicationService_Validation_Tests : AbpIntegratedTest + context.Services.OnRegistered(onServiceRegistredContext => { if (typeof(IMyAppService).IsAssignableFrom(onServiceRegistredContext.ImplementationType) && !DynamicProxyIgnoreTypes.Contains(onServiceRegistredContext.ImplementationType)) diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json index 4a13985c8a..8749163c1d 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json @@ -3,16 +3,16 @@ "texts": { "Menu:Account": "帳號", "UserName": "使用者名稱", - "EmailAddress": "電子信箱地址", - "UserNameOrEmailAddress": "使用者名稱或電子信箱地址", + "EmailAddress": "電子郵件地址", + "UserNameOrEmailAddress": "使用者名稱或電子郵件地址", "Password": "密碼", "RememberMe": "記住我", "UseAnotherServiceToLogin": "使用另一個服務登入", "UserLockedOutMessage": "登入失敗,使用者帳號已被鎖定.請稍後再試.", "InvalidUserNameOrPassword": "使用者名稱或密碼錯誤!", - "LoginIsNotAllowed": "無法登入!你的賬號未激活或者需要驗證郵箱地址/手機號碼.", + "LoginIsNotAllowed": "無法登入!你的帳號未啟用或者需要驗證電子郵件地址/手機號碼.", "SelfRegistrationDisabledMessage": "應用程式未開放註冊,請聯絡管理員以加入新使用者.", - "LocalLoginDisabledMessage": "應用程序未開放本地賬戶登錄.", + "LocalLoginDisabledMessage": "應用程式未開放本地帳戶登入.", "Login": "登入", "Cancel": "取消", "Register": "註冊", @@ -26,11 +26,11 @@ "DisplayName:NewPasswordConfirm": "確認新密碼", "PasswordChangedMessage": "你的密碼已修改成功.", "DisplayName:UserName": "使用者名稱", - "DisplayName:Email": "電子信箱", + "DisplayName:Email": "電子郵件信箱", "DisplayName:Name": "名字", "DisplayName:Surname": "姓", "DisplayName:Password": "密碼", - "DisplayName:EmailAddress": "電子信箱地址", + "DisplayName:EmailAddress": "電子郵件地址", "DisplayName:PhoneNumber": "電話號碼", "PersonalSettings": "個人設置", "PersonalSettingsSaved": "個人設置已保存", @@ -38,32 +38,32 @@ "NewPasswordConfirmFailed": "請確認新密碼", "NewPasswordSameAsOld": "新密碼不能與舊密碼相同", "Manage": "管理", - "MyAccount": "我的賬戶", + "MyAccount": "我的帳戶", "DisplayName:Abp.Account.IsSelfRegistrationEnabled": "啟用自行註冊", "Description:Abp.Account.IsSelfRegistrationEnabled": "是否允許使用者自行註冊帳號.", "DisplayName:Abp.Account.EnableLocalLogin": "使用本地帳號進行身分驗證", "Description:Abp.Account.EnableLocalLogin": "伺服器是否允許使用者使用本地帳號進行身份驗證。", "LoggedOutTitle": "註銷", "LoggedOutText": "你已成功註銷並將馬上返回.", - "ReturnToText": "單擊此處返回到應用程序", + "ReturnToText": "點擊此處返回到應用程式", "OrLoginWith": "或是登入用:", "ForgotPassword": "忘記密碼?", - "SendPasswordResetLink_Information": "密碼重置鏈接將發送到您的電子郵件以重置密碼. 如果您在幾分鐘內沒有收到電子郵件,請重試.", + "SendPasswordResetLink_Information": "密碼重置連結將發送到您的電子郵件以重置密碼. 如果您在幾分鐘內沒有收到電子郵件,請重試.", "PasswordResetMailSentMessage": "帳戶恢復電子郵件已發送到您的電子郵件地址. 如果您在15分鐘內未在收件箱中看到此電子郵件,請檢查垃圾郵件,並標記為非垃圾郵件.", "ResetPassword": "重設密碼", "ConfirmPassword": "確認密碼", "ResetPassword_Information": "請輸入您的新密碼.", "YourPasswordIsSuccessfullyReset": "您的密碼已經被重置成功.", - "GoToTheApplication": "轉到應用程序", - "BackToLogin": "返回登錄", + "GoToTheApplication": "轉到應用程式", + "BackToLogin": "返回登入", "ProfileTab:Password": "更改密碼", - "ProfileTab:PersonalInfo": "個人信息", - "ReturnToApplication": "返回到應用程序", + "ProfileTab:PersonalInfo": "個人資訊", + "ReturnToApplication": "返回到應用程式", "Volo.Account:InvalidEmailAddress": "找不到給定的電子郵件地址:{0}", "PasswordReset": "重設密碼", - "PasswordResetInfoInEmail": "我們收到了帳戶恢復請求!如果你發起了此請求,請單擊以下鏈接以重置密碼.", + "PasswordResetInfoInEmail": "我們收到了帳戶恢復請求!如果你發起了此請求,請點擊以下連結以重置密碼.", "ResetMyPassword": "重置我的密碼", "AccessDenied": "拒絕訪問!", "AccessDeniedMessage": "您無權訪問此資源." } -} \ No newline at end of file +} diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs index 1920cdd8ad..ce1ecd0e82 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs @@ -1,6 +1,8 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Account.Settings; using Volo.Abp.Identity; +using Volo.Abp.Settings; namespace Volo.Abp.Account.Web.Pages.Account; @@ -28,7 +30,12 @@ public class LogoutModel : AccountPageModel return RedirectSafely(ReturnUrl, ReturnUrlHash); } - return RedirectToPage("/Account/Login"); + if (await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin)) + { + return RedirectToPage("/Account/Login"); + } + + return RedirectToPage("/"); } public virtual Task OnPostAsync() diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.HangFire/Program.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.HangFire/Program.cs index 5f2c23ceb4..244063d37f 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.HangFire/Program.cs +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.HangFire/Program.cs @@ -1,23 +1,40 @@ using System; +using System.Threading; +using System.Threading.Tasks; +using Hangfire; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs; +using Volo.Abp.Threading; namespace Volo.Abp.BackgroundJobs.DemoApp.HangFire; class Program { - static void Main(string[] args) + async static Task Main(string[] args) { - using (var application = AbpApplicationFactory.Create(options => + using (var application = await AbpApplicationFactory.CreateAsync(options => + { + options.UseAutofac(); + })) { - options.UseAutofac(); - })) - { - application.Initialize(); + await application.InitializeAsync(); + + await CancelableBackgroundJobAsync(application.ServiceProvider); Console.WriteLine("Started: " + typeof(Program).Namespace); Console.WriteLine("Press ENTER to stop the application..!"); Console.ReadLine(); - application.Shutdown(); + await application.ShutdownAsync(); } } + + private async static Task CancelableBackgroundJobAsync(IServiceProvider serviceProvider) + { + var backgroundJobManager = serviceProvider.GetRequiredService(); + var jobId = await backgroundJobManager.EnqueueAsync(new LongRunningJobArgs { Value = "test-1" }); + await backgroundJobManager.EnqueueAsync(new LongRunningJobArgs { Value = "test-2" }); + Thread.Sleep(1000); + BackgroundJob.Delete(jobId); + } } diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Quartz/Program.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Quartz/Program.cs index 1ececcd621..eebe5fa279 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Quartz/Program.cs +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Quartz/Program.cs @@ -1,23 +1,41 @@ using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Quartz; +using Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs; +using Volo.Abp.Threading; namespace Volo.Abp.BackgroundJobs.DemoApp.Quartz; class Program { - static void Main(string[] args) + async static Task Main(string[] args) { - using (var application = AbpApplicationFactory.Create(options => + using (var application = await AbpApplicationFactory.CreateAsync(options => { options.UseAutofac(); })) { - application.Initialize(); + await application.InitializeAsync(); + + await CancelableBackgroundJobAsync(application.ServiceProvider); Console.WriteLine("Started: " + typeof(Program).Namespace); Console.WriteLine("Press ENTER to stop the application..!"); Console.ReadLine(); - application.Shutdown(); + await application.ShutdownAsync(); } } + + private async static Task CancelableBackgroundJobAsync(IServiceProvider serviceProvider) + { + var backgroundJobManager = serviceProvider.GetRequiredService(); + var jobId = await backgroundJobManager.EnqueueAsync(new LongRunningJobArgs {Value = "test-1"}); + await backgroundJobManager.EnqueueAsync(new LongRunningJobArgs { Value = "test-2" }); + Thread.Sleep(1000); + var scheduler = serviceProvider.GetRequiredService(); + await scheduler.Interrupt(new JobKey(jobId.Split('.')[1],jobId.Split('.')[0])); + } } diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.RabbitMq/Program.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.RabbitMq/Program.cs index 5d04169c15..a07a0498f9 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.RabbitMq/Program.cs +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.RabbitMq/Program.cs @@ -1,23 +1,24 @@ using System; +using System.Threading.Tasks; namespace Volo.Abp.BackgroundJobs.DemoApp.RabbitMq; class Program { - static void Main(string[] args) + async static Task Main(string[] args) { - using (var application = AbpApplicationFactory.Create(options => + using (var application = await AbpApplicationFactory.CreateAsync(options => { options.UseAutofac(); })) { - application.Initialize(); + await application.InitializeAsync(); Console.WriteLine("Started: " + typeof(Program).Namespace); Console.WriteLine("Press ENTER to stop the application..!"); Console.ReadLine(); - application.Shutdown(); + await application.ShutdownAsync(); } } } diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/LongRunningJob.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/LongRunningJob.cs new file mode 100644 index 0000000000..9a57331fff --- /dev/null +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/LongRunningJob.cs @@ -0,0 +1,49 @@ +using System; +using System.Threading; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; + +namespace Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs +{ + public class LongRunningJob : BackgroundJob, ITransientDependency + { + private readonly ICancellationTokenProvider _cancellationTokenProvider; + + public LongRunningJob(ICancellationTokenProvider cancellationTokenProvider) + { + _cancellationTokenProvider = cancellationTokenProvider; + } + + public override void Execute(LongRunningJobArgs args) + { + lock (Console.Out) + { + var oldColor = Console.ForegroundColor; + try + { + Console.WriteLine($"Long running {args.Value} start: {DateTime.Now}"); + + for (var i = 1; i <= 10; i++) + { + _cancellationTokenProvider.Token.ThrowIfCancellationRequested(); + + Thread.Sleep(1000); + Console.WriteLine($"{args.Value} step-{i} done: {DateTime.Now}"); + } + + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine($"Long running {args.Value} completed: {DateTime.Now}"); + } + catch (OperationCanceledException) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"Long running {args.Value} cancelled!!!"); + } + finally + { + Console.ForegroundColor = oldColor; + } + } + } + } +} \ No newline at end of file diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/LongRunningJobArgs.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/LongRunningJobArgs.cs new file mode 100644 index 0000000000..fabba0fa57 --- /dev/null +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/LongRunningJobArgs.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs +{ + [BackgroundJobName("LongJob")] + public class LongRunningJobArgs + { + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/WriteToConsoleGreenJob.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/WriteToConsoleGreenJob.cs index e065e409e5..5e5c00a97d 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/WriteToConsoleGreenJob.cs +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/WriteToConsoleGreenJob.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Volo.Abp.DependencyInjection; namespace Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/WriteToConsoleYellowJob.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/WriteToConsoleYellowJob.cs index a610b6e75a..dbeae26552 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/WriteToConsoleYellowJob.cs +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/WriteToConsoleYellowJob.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Volo.Abp.DependencyInjection; namespace Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/DemoAppModule.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/DemoAppModule.cs index a909fb476e..976705b07d 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/DemoAppModule.cs +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/DemoAppModule.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Autofac; +using System.Threading.Tasks; +using Volo.Abp.Autofac; using Volo.Abp.BackgroundJobs.DemoApp.Shared; using Volo.Abp.BackgroundJobs.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; @@ -27,19 +28,21 @@ public class DemoAppModule : AbpModule Configure(options => { - //Configure for fast running - options.JobPollPeriod = 1000; + //Configure for fast running + options.JobPollPeriod = 1000; options.DefaultFirstWaitDuration = 1; options.DefaultWaitFactor = 1; }); } - public override void OnApplicationInitialization(ApplicationInitializationContext context) + public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context) { //TODO: Configure console logging //context // .ServiceProvider // .GetRequiredService() // .AddConsole(LogLevel.Debug); + + return Task.CompletedTask; } } diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Program.cs b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Program.cs index f895e7adbb..f8fa05863d 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Program.cs +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Program.cs @@ -1,23 +1,23 @@ using System; - +using System.Threading.Tasks; namespace Volo.Abp.BackgroundJobs.DemoApp; class Program { - static void Main(string[] args) + async static Task Main(string[] args) { - using (var application = AbpApplicationFactory.Create(options => + using (var application = await AbpApplicationFactory.CreateAsync(options => { options.UseAutofac(); })) { - application.Initialize(); + await application.InitializeAsync(); Console.WriteLine("Started: " + typeof(Program).Namespace); Console.WriteLine("Press ENTER to stop the application..!"); Console.ReadLine(); - application.Shutdown(); + await application.ShutdownAsync(); } } } diff --git a/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/FirstLevelNavMenuItem.razor b/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/FirstLevelNavMenuItem.razor index b3ecb41862..1f469a809a 100644 --- a/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/FirstLevelNavMenuItem.razor +++ b/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/FirstLevelNavMenuItem.razor @@ -13,7 +13,7 @@ { } - else if (url != null) + else if (MenuItem.Url != null) { public virtual int AccessFailedCount { get; protected internal set; } + /// + /// Should change password on next login. + /// + public virtual bool ShouldChangePasswordOnNextLogin { get; protected internal set; } + /// /// A version value that is increased whenever the entity is changed. /// @@ -368,6 +373,11 @@ public class IdentityUser : FullAuditedAggregateRoot, IUser, IHasEntityVer IsActive = isActive; } + public virtual void SetShouldChangePasswordOnNextLogin(bool shouldChangePasswordOnNextLogin) + { + ShouldChangePasswordOnNextLogin = shouldChangePasswordOnNextLogin; + } + public override string ToString() { return $"{base.ToString()}, UserName = {UserName}"; diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityLinkUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityLinkUserRepository.cs index 6a0245fa4f..035904120c 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityLinkUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityLinkUserRepository.cs @@ -20,6 +20,7 @@ public class EfCoreIdentityLinkUserRepository : EfCoreRepository FindAsync(IdentityLinkUserInfo sourceLinkUserInfo, IdentityLinkUserInfo targetLinkUserInfo, CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) + .AsNoTracking() .OrderBy(x => x.Id).FirstOrDefaultAsync(x => x.SourceUserId == sourceLinkUserInfo.UserId && x.SourceTenantId == sourceLinkUserInfo.TenantId && x.TargetUserId == targetLinkUserInfo.UserId && x.TargetTenantId == targetLinkUserInfo.TenantId || @@ -31,7 +32,8 @@ public class EfCoreIdentityLinkUserRepository : EfCoreRepository> GetListAsync(IdentityLinkUserInfo linkUserInfo, List excludes = null, CancellationToken cancellationToken = default) { - IQueryable query = (await GetDbSetAsync()) + var query = (await GetDbSetAsync()) + .AsNoTracking() .Where(x => x.SourceUserId == linkUserInfo.UserId && x.SourceTenantId == linkUserInfo.TenantId || x.TargetUserId == linkUserInfo.UserId && x.TargetTenantId == linkUserInfo.TenantId); @@ -51,7 +53,7 @@ public class EfCoreIdentityLinkUserRepository : EfCoreRepository + var linkUsers = await (await GetDbSetAsync()).AsNoTracking().Where(x => x.SourceUserId == linkUserInfo.UserId && x.SourceTenantId == linkUserInfo.TenantId || x.TargetUserId == linkUserInfo.UserId && x.TargetTenantId == linkUserInfo.TenantId) .ToListAsync(cancellationToken: GetCancellationToken(cancellationToken)); diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs index e2527555b2..60abbb3054 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs @@ -348,4 +348,12 @@ public class EfCoreIdentityUserRepository : EfCoreRepository> GetListByIdsAsync(IEnumerable ids, bool includeDetails = false, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .IncludeDetails(includeDetails) + .Where(x => ids.Contains(x.Id)) + .ToListAsync(GetCancellationToken(cancellationToken)); + } } diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs index ac2ab63809..ae805d9a03 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs @@ -316,4 +316,11 @@ public class MongoIdentityUserRepository : MongoDbRepository> GetListByIdsAsync(IEnumerable ids, bool includeDetails = false, CancellationToken cancellationToken = default) + { + return await (await GetMongoQueryableAsync(cancellationToken)) + .Where(x => ids.Contains(x.Id)) + .ToListAsync(GetCancellationToken(cancellationToken)); + } } 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 cf69fda91a..6e738df9be 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 @@ -122,6 +122,13 @@ public class AbpResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator else if (result.IsNotAllowed) { Logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", context.UserName); + + if (user.ShouldChangePasswordOnNextLogin) + { + await HandleShouldChangePasswordOnNextLoginAsync(context, user, context.Password); + return; + } + errorDescription = Localizer["LoginIsNotAllowed"]; } else @@ -193,6 +200,61 @@ public class AbpResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator } } + protected virtual async Task HandleShouldChangePasswordOnNextLoginAsync(ResourceOwnerPasswordValidationContext context, IdentityUser user, string currentPassword) + { + var changePasswordToken = context.Request?.Raw?["ChangePasswordToken"]; + var newPassword = context.Request?.Raw?["NewPassword"]; + if (!changePasswordToken.IsNullOrWhiteSpace() && !currentPassword.IsNullOrWhiteSpace() && !newPassword.IsNullOrWhiteSpace()) + { + if (await UserManager.VerifyUserTokenAsync(user, TokenOptions.DefaultProvider, nameof(IdentityUser.ShouldChangePasswordOnNextLogin), changePasswordToken)) + { + var changePasswordResult = await UserManager.ChangePasswordAsync(user, currentPassword, newPassword); + if (changePasswordResult.Succeeded) + { + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext + { + Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, + Action = IdentitySecurityLogActionConsts.ChangePassword, + UserName = context.UserName, + ClientId = await FindClientIdAsync(context) + }); + + user.SetShouldChangePasswordOnNextLogin(false); + await UserManager.UpdateAsync(user); + await SetSuccessResultAsync(context, user); + } + else + { + Logger.LogInformation("ChangePassword failed for username: {username}, reason: {changePasswordResult}", context.UserName, changePasswordResult); + context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, changePasswordResult.Errors.Select(x => x.Description).JoinAsString(", ")); + } + } + else + { + Logger.LogInformation("Authentication failed for username: {username}, reason: InvalidAuthenticatorCode", context.UserName); + context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, Localizer["InvalidAuthenticatorCode"]); + } + } + else + { + Logger.LogInformation("Authentication failed for username: {username}, reason: {ShouldChangePasswordOnNextLogin}", context.UserName, nameof(user.ShouldChangePasswordOnNextLogin)); + context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, nameof(user.ShouldChangePasswordOnNextLogin), + new Dictionary() + { + {"userId", user.Id}, + {"changePasswordToken", await UserManager.GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, nameof(IdentityUser.ShouldChangePasswordOnNextLogin))} + }); + + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext + { + Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, + Action = IdentityServerSecurityLogActionConsts.LoginNotAllowed, + UserName = context.UserName, + ClientId = await FindClientIdAsync(context) + }); + } + } + protected virtual async Task SetSuccessResultAsync(ResourceOwnerPasswordValidationContext context, IdentityUser user) { var sub = await UserManager.GetUserIdAsync(user); diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20230106050616_Initial.Designer.cs b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20230307054116_Initial.Designer.cs similarity index 99% rename from modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20230106050616_Initial.Designer.cs rename to modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20230307054116_Initial.Designer.cs index 64ad616624..bc14c07aec 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20230106050616_Initial.Designer.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20230307054116_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace OpenIddict.Demo.Server.Migrations { [DbContext(typeof(ServerDbContext))] - [Migration("20230106050616_Initial")] + [Migration("20230307054116_Initial")] partial class Initial { /// @@ -235,6 +235,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -421,6 +424,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasDefaultValue(false) .HasColumnName("EmailConfirmed"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -497,6 +503,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") @@ -699,6 +708,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("nvarchar(128)") .HasColumnName("DisplayName"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -1277,6 +1289,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("datetime2") .HasColumnName("DeletionTime"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20230106050616_Initial.cs b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20230307054116_Initial.cs similarity index 99% rename from modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20230106050616_Initial.cs rename to modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20230307054116_Initial.cs index 611a9ad2ca..ab3646a62c 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20230106050616_Initial.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20230307054116_Initial.cs @@ -106,6 +106,7 @@ namespace OpenIddict.Demo.Server.Migrations ParentId = table.Column(type: "uniqueidentifier", nullable: true), Code = table.Column(type: "nvarchar(95)", maxLength: 95, nullable: false), DisplayName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), CreationTime = table.Column(type: "datetime2", nullable: false), @@ -186,6 +187,7 @@ namespace OpenIddict.Demo.Server.Migrations IsDefault = table.Column(type: "bit", nullable: false), IsStatic = table.Column(type: "bit", nullable: false), IsPublic = table.Column(type: "bit", nullable: false), + EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true) }, @@ -240,6 +242,7 @@ namespace OpenIddict.Demo.Server.Migrations { Id = table.Column(type: "uniqueidentifier", nullable: false), Name = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), CreationTime = table.Column(type: "datetime2", nullable: false), @@ -278,6 +281,8 @@ namespace OpenIddict.Demo.Server.Migrations LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), AccessFailedCount = table.Column(type: "int", nullable: false, defaultValue: 0), + ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), + EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), CreationTime = table.Column(type: "datetime2", nullable: false), diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs index c0599ebe15..f5e599fe73 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs @@ -232,6 +232,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -418,6 +421,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasDefaultValue(false) .HasColumnName("EmailConfirmed"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -494,6 +500,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") @@ -696,6 +705,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("nvarchar(128)") .HasColumnName("DisplayName"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -1274,6 +1286,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("datetime2") .HasColumnName("DeletionTime"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj index d3f9e0e9cb..f9c802d0e6 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj @@ -20,8 +20,8 @@ - - - + + + diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs index d7eacfa498..8a664da5c1 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs @@ -127,6 +127,9 @@ public class AbpOpenIddictAspNetCoreModule : AbpModule builder.RemoveEventHandler(OpenIddictServerHandlers.Session.ValidatePostLogoutRedirectUriParameter.Descriptor); builder.AddEventHandler(AbpValidatePostLogoutRedirectUriParameter.Descriptor); + + builder.RemoveEventHandler(OpenIddictServerHandlers.Session.ValidateAuthorizedParty.Descriptor); + builder.AddEventHandler(AbpValidateAuthorizedParty.Descriptor); } builder.AddEventHandler(RemoveClaimsFromClientCredentialsGrantType.Descriptor); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs index 6406c935c7..3e9168e7fb 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs @@ -101,6 +101,12 @@ public partial class TokenController else if (result.IsNotAllowed) { Logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", request.Username); + + if (user.ShouldChangePasswordOnNextLogin) + { + return await HandleShouldChangePasswordOnNextLoginAsync(request, user, request.Password); + } + errorDescription = "You are not allowed to login! Your account is inactive or needs to confirm your email/phone number."; } else @@ -197,8 +203,7 @@ public partial class TokenController items: new Dictionary { [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = - nameof(SignInResult.RequiresTwoFactor), + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = nameof(SignInResult.RequiresTwoFactor) }, parameters: new Dictionary { @@ -210,6 +215,82 @@ public partial class TokenController } } + protected virtual async Task HandleShouldChangePasswordOnNextLoginAsync(OpenIddictRequest request, IdentityUser user, string currentPassword) + { + var changePasswordToken = request.GetParameter("ChangePasswordToken")?.ToString(); + var newPassword = request.GetParameter("NewPassword")?.ToString(); + if (!changePasswordToken.IsNullOrWhiteSpace() && !currentPassword.IsNullOrWhiteSpace() && !newPassword.IsNullOrWhiteSpace()) + { + if (await UserManager.VerifyUserTokenAsync(user, TokenOptions.DefaultProvider, nameof(IdentityUser.ShouldChangePasswordOnNextLogin), changePasswordToken)) + { + var changePasswordResult = await UserManager.ChangePasswordAsync(user, currentPassword, newPassword); + if (changePasswordResult.Succeeded) + { + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext + { + Identity = OpenIddictSecurityLogIdentityConsts.OpenIddict, + Action = IdentitySecurityLogActionConsts.ChangePassword, + UserName = request.Username, + ClientId = request.ClientId + }); + + user.SetShouldChangePasswordOnNextLogin(false); + await UserManager.UpdateAsync(user); + return await SetSuccessResultAsync(request, user); + } + else + { + Logger.LogInformation("ChangePassword failed for username: {username}, reason: {changePasswordResult}", request.Username, changePasswordResult.Errors.Select(x => x.Description).JoinAsString(", ")); + + var properties = new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = changePasswordResult.Errors.Select(x => x.Description).JoinAsString(", ") + }); + return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } + } + else + { + Logger.LogInformation("Authentication failed for username: {username}, reason: InvalidAuthenticatorCode", request.Username); + + var properties = new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "Invalid authenticator code!" + }); + + return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } + } + else + { + Logger.LogInformation("Authentication failed for username: {username}, reason: {ShouldChangePasswordOnNextLogin}", request.Username, nameof(user.ShouldChangePasswordOnNextLogin)); + + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext + { + Identity = OpenIddictSecurityLogIdentityConsts.OpenIddict, + Action = OpenIddictSecurityLogActionConsts.LoginNotAllowed, + UserName = request.Username, + ClientId = request.ClientId + }); + + var properties = new AuthenticationProperties( + items: new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = nameof(IdentityUser.ShouldChangePasswordOnNextLogin) + }, + parameters: new Dictionary + { + ["userId"] = user.Id.ToString("N"), + ["changePasswordToken"] = await UserManager.GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, nameof(IdentityUser.ShouldChangePasswordOnNextLogin)) + }); + + return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } + } + protected virtual async Task SetSuccessResultAsync(OpenIddictRequest request, IdentityUser user) { // Create a new ClaimsPrincipal containing the claims that diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpOpenIddictWildcardDomainBase.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpOpenIddictWildcardDomainBase.cs index 2a603b8f44..8839871832 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpOpenIddictWildcardDomainBase.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpOpenIddictWildcardDomainBase.cs @@ -7,19 +7,16 @@ using Volo.Abp.Text.Formatting; namespace Volo.Abp.OpenIddict.WildcardDomains; public abstract class AbpOpenIddictWildcardDomainBase : IOpenIddictServerHandler - where THandler : class, new() + where THandler : class where TContext : OpenIddictServerEvents.BaseContext { protected THandler Handler { get; set; } protected AbpOpenIddictWildcardDomainOptions WildcardDomainOptions { get; } - protected AbpOpenIddictWildcardDomainBase(IOptions wildcardDomainOptions, bool initHandler = true) + protected AbpOpenIddictWildcardDomainBase(IOptions wildcardDomainOptions, THandler handler) { WildcardDomainOptions = wildcardDomainOptions.Value; - if (initHandler) - { - Handler = new THandler(); - } + Handler = handler; } public abstract ValueTask HandleAsync(TContext context); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateAuthorizedParty.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateAuthorizedParty.cs new file mode 100644 index 0000000000..81c7d66788 --- /dev/null +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateAuthorizedParty.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using OpenIddict.Abstractions; +using OpenIddict.Server; + +namespace Volo.Abp.OpenIddict.WildcardDomains; + +public class AbpValidateAuthorizedParty : AbpOpenIddictWildcardDomainBase +{ + public static OpenIddictServerHandlerDescriptor Descriptor { get; } + = OpenIddictServerHandlerDescriptor.CreateBuilder() + .UseScopedHandler() + .SetOrder(OpenIddictServerHandlers.Session.ValidateToken.Descriptor.Order + 1_000) + .SetType(OpenIddictServerHandlerType.BuiltIn) + .Build(); + + public AbpValidateAuthorizedParty( + IOptions wildcardDomainsOptions, + IOpenIddictApplicationManager applicationManager) + : base(wildcardDomainsOptions, new OpenIddictServerHandlers.Session.ValidateAuthorizedParty(applicationManager)) + { + Handler = new OpenIddictServerHandlers.Session.ValidateAuthorizedParty(applicationManager); + } + + public async override ValueTask HandleAsync(OpenIddictServerEvents.ValidateLogoutRequestContext context) + { + Check.NotNull(context, nameof(context)); + Check.NotNull(context.IdentityTokenHintPrincipal, nameof(context.IdentityTokenHintPrincipal)); + + if (await CheckWildcardDomainAsync(context.PostLogoutRedirectUri)) + { + return; + } + + await Handler.HandleAsync(context); + } +} diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientPostLogoutRedirectUri.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientPostLogoutRedirectUri.cs index 527eb05eb0..e1fe8b94f8 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientPostLogoutRedirectUri.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientPostLogoutRedirectUri.cs @@ -19,7 +19,7 @@ public class AbpValidateClientPostLogoutRedirectUri : AbpOpenIddictWildcardDomai public AbpValidateClientPostLogoutRedirectUri( IOptions wildcardDomainsOptions, IOpenIddictApplicationManager applicationManager) - : base(wildcardDomainsOptions, false) + : base(wildcardDomainsOptions, new OpenIddictServerHandlers.Session.ValidateClientPostLogoutRedirectUri(applicationManager)) { Handler = new OpenIddictServerHandlers.Session.ValidateClientPostLogoutRedirectUri(applicationManager); } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientRedirectUri.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientRedirectUri.cs index 85d82a95ab..d2871a57cc 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientRedirectUri.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientRedirectUri.cs @@ -18,7 +18,7 @@ public class AbpValidateClientRedirectUri : AbpOpenIddictWildcardDomainBase wildcardDomainsOptions, IOpenIddictApplicationManager applicationManager) - : base(wildcardDomainsOptions, false) + : base(wildcardDomainsOptions, new OpenIddictServerHandlers.Authentication.ValidateClientRedirectUri()) { Handler = new OpenIddictServerHandlers.Authentication.ValidateClientRedirectUri(applicationManager); } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidatePostLogoutRedirectUriParameter.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidatePostLogoutRedirectUriParameter.cs index 83f33d015c..c6bfc155b5 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidatePostLogoutRedirectUriParameter.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidatePostLogoutRedirectUriParameter.cs @@ -14,7 +14,7 @@ public class AbpValidatePostLogoutRedirectUriParameter : AbpOpenIddictWildcardDo .Build(); public AbpValidatePostLogoutRedirectUriParameter(IOptions wildcardDomainsOptions) - : base(wildcardDomainsOptions) + : base(wildcardDomainsOptions, new OpenIddictServerHandlers.Session.ValidatePostLogoutRedirectUriParameter()) { } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateRedirectUriParameter.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateRedirectUriParameter.cs index 5354646b56..3bac84e2c4 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateRedirectUriParameter.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateRedirectUriParameter.cs @@ -14,7 +14,7 @@ public class AbpValidateRedirectUriParameter : AbpOpenIddictWildcardDomainBase wildcardDomainsOptions) - : base(wildcardDomainsOptions) + : base(wildcardDomainsOptions, new OpenIddictServerHandlers.Authentication.ValidateRedirectUriParameter()) { } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj index d7cb8d2592..267b753fcb 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj @@ -14,7 +14,7 @@ - + diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj index 456ea01da2..f51336b37c 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj @@ -17,7 +17,7 @@ - + diff --git a/npm/ng-packs/angular.json b/npm/ng-packs/angular.json index 01e482cd85..14416d1d3a 100644 --- a/npm/ng-packs/angular.json +++ b/npm/ng-packs/angular.json @@ -195,14 +195,54 @@ "assets": ["apps/dev-app/src/favicon.ico", "apps/dev-app/src/assets"], "styles": [ { - "input": "node_modules/bootstrap/dist/css/bootstrap.rtl.min.css", + "input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/bootstrap-dim.css", "inject": false, - "bundleName": "bootstrap-rtl.min" + "bundleName": "bootstrap-dim" }, { - "input": "node_modules/bootstrap/dist/css/bootstrap.min.css", - "inject": true, - "bundleName": "bootstrap-ltr.min" + "input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/ng-bundle.css", + "inject": false, + "bundleName": "ng-bundle" + }, + { + "input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/side-menu/layout-bundle.css", + "inject": false, + "bundleName": "layout-bundle" + }, + { + "input": "node_modules/@abp/ng.theme.lepton-x/assets/css/abp-bundle.css", + "inject": false, + "bundleName": "abp-bundle" + }, + { + "input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/bootstrap-dim.rtl.css", + "inject": false, + "bundleName": "bootstrap-dim.rtl" + }, + { + "input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/ng-bundle.rtl.css", + "inject": false, + "bundleName": "ng-bundle.rtl" + }, + { + "input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/side-menu/layout-bundle.rtl.css", + "inject": false, + "bundleName": "layout-bundle.rtl" + }, + { + "input": "node_modules/@abp/ng.theme.lepton-x/assets/css/abp-bundle.rtl.css", + "inject": false, + "bundleName": "abp-bundle.rtl" + }, + { + "input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/font-bundle.rtl.css", + "inject": false, + "bundleName": "font-bundle.rtl" + }, + { + "input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/font-bundle.css", + "inject": false, + "bundleName": "font-bundle" }, { "input": "node_modules/@fortawesome/fontawesome-free/css/all.min.css", @@ -234,6 +274,7 @@ "inject": false, "bundleName": "ng-zorro-antd-tree" }, + "node_modules/bootstrap-icons/font/bootstrap-icons.css", "apps/dev-app/src/styles.scss" ], "scripts": [] @@ -715,7 +756,7 @@ } }, "tags": [], - "implicitDependencies": ["core","oauth"] + "implicitDependencies": ["core", "oauth"] } } } diff --git a/npm/ng-packs/apps/dev-app/src/app/app.module.ts b/npm/ng-packs/apps/dev-app/src/app/app.module.ts index 52ebd38362..56e38b40ee 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app.module.ts @@ -4,7 +4,8 @@ import { registerLocale } from '@abp/ng.core/locale'; import { IdentityConfigModule } from '@abp/ng.identity/config'; import { SettingManagementConfigModule } from '@abp/ng.setting-management/config'; import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config'; -import { ThemeBasicModule } from '@abp/ng.theme.basic'; +import { ThemeLeptonXModule } from '@abp/ng.theme.lepton-x'; +import { SideMenuLayoutModule } from '@abp/ng.theme.lepton-x/layouts'; import { ThemeSharedModule } from '@abp/ng.theme.shared'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; @@ -34,7 +35,8 @@ import { AbpOAuthModule } from '@abp/ng.oauth'; TenantManagementConfigModule.forRoot(), FeatureManagementModule.forRoot(), SettingManagementConfigModule.forRoot(), - ThemeBasicModule.forRoot(), + ThemeLeptonXModule.forRoot(), + SideMenuLayoutModule.forRoot(), ], providers: [APP_ROUTE_PROVIDER], declarations: [AppComponent], diff --git a/npm/ng-packs/apps/dev-app/src/app/home/home.component.html b/npm/ng-packs/apps/dev-app/src/app/home/home.component.html index 2d249da3d8..7024ab69e5 100644 --- a/npm/ng-packs/apps/dev-app/src/app/home/home.component.html +++ b/npm/ng-packs/apps/dev-app/src/app/home/home.component.html @@ -26,8 +26,8 @@

Let's improve your application!

Here are some links to help you get started:

-
-
+ +
-
-
+ +

Meet the ABP Commercial

A Complete Web Application Platform Built on the ABP Framework

- -
-
+ +

ABP Commercial is a platform based on the open source ABP framework. It provides pre-built application modules, rapid @@ -271,8 +270,9 @@ " >

-
- + + + -
diff --git a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts index cb0cdd9755..6dd1ab9f17 100644 --- a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts +++ b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts @@ -1,12 +1,28 @@ -import { ListService, PagedResultDto } from "@abp/ng.core"; -import { GetIdentityUsersInput, IdentityRoleDto, IdentityUserDto, IdentityUserService } from "@abp/ng.identity/proxy"; -import { ePermissionManagementComponents } from "@abp/ng.permission-management"; -import { Confirmation, ConfirmationService, ToasterService } from "@abp/ng.theme.shared"; -import { EXTENSIONS_IDENTIFIER, FormPropData, generateFormFromProps } from "@abp/ng.theme.shared/extensions"; -import { Component, Injector, OnInit, TemplateRef, TrackByFunction, ViewChild } from "@angular/core"; -import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from "@angular/forms"; -import { finalize, switchMap, tap } from "rxjs/operators"; -import { eIdentityComponents } from "../../enums/components"; +import { ListService, PagedResultDto } from '@abp/ng.core'; +import { + GetIdentityUsersInput, + IdentityRoleDto, + IdentityUserDto, + IdentityUserService, +} from '@abp/ng.identity/proxy'; +import { ePermissionManagementComponents } from '@abp/ng.permission-management'; +import {Confirmation, ConfirmationService, eFormComponets, ToasterService} from '@abp/ng.theme.shared'; +import { + EXTENSIONS_IDENTIFIER, + FormPropData, + generateFormFromProps, +} from '@abp/ng.theme.shared/extensions'; +import { + Component, + Injector, + OnInit, + TemplateRef, + TrackByFunction, + ViewChild, +} from '@angular/core'; +import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { finalize, switchMap, tap } from 'rxjs/operators'; +import { eIdentityComponents } from '../../enums/components'; @Component({ selector: 'abp-users', @@ -43,7 +59,9 @@ export class UsersComponent implements OnInit { permissionManagementKey = ePermissionManagementComponents.PermissionManagement; - entityDisplayName?: string; + entityDisplayName: string; + + inputKey=eFormComponets.FormCheckboxComponent trackByFn: TrackByFunction = (index, item) => Object.keys(item)[0] || index; diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts b/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts index 6193a5bc20..5a3373121c 100644 --- a/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts +++ b/npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts @@ -81,7 +81,7 @@ export class PermissionManagementComponent }); }); } else { - this.selectedGroup = null; + this.setSelectedGroup(null); this._visible = false; this.visibleChange.emit(false); } @@ -110,10 +110,22 @@ export class PermissionManagementComponent modalBusy = false; + selectedGroupPermissions: PermissionWithStyle[] = []; + trackByFn: TrackByFunction = (_, item) => item.name; - get selectedGroupPermissions(): PermissionWithStyle[] { - if (!this.selectedGroup) return []; + constructor(protected service: PermissionsService, protected configState: ConfigStateService) {} + + getChecked(name: string) { + return (this.permissions.find(per => per.name === name) || { isGranted: false }).isGranted; + } + + setSelectedGroup(group: PermissionGroupDto) { + this.selectedGroup = group; + if (!this.selectedGroup) { + this.selectedGroupPermissions = []; + return; + } const margin = `margin-${ (document.body.dir as LocaleDirection) === 'rtl' ? 'right' : 'left' @@ -123,7 +135,7 @@ export class PermissionManagementComponent (this.data.groups.find(group => group.name === this.selectedGroup?.name) || {}).permissions || []; - return permissions.map( + this.selectedGroupPermissions = permissions.map( permission => ({ ...permission, @@ -133,12 +145,6 @@ export class PermissionManagementComponent ); } - constructor(protected service: PermissionsService, protected configState: ConfigStateService) {} - - getChecked(name: string) { - return (this.permissions.find(per => per.name === name) || { isGranted: false }).isGranted; - } - setDisabled(permissions: PermissionGrantInfoDto[]) { if (permissions.length) { this.disableSelectAllTab = permissions.every( @@ -248,7 +254,7 @@ export class PermissionManagementComponent onChangeGroup(group: PermissionGroupDto) { this.setDisabled(group.permissions); - this.selectedGroup = group; + this.setSelectedGroup(group); this.setTabCheckboxState(); } @@ -291,8 +297,8 @@ export class PermissionManagementComponent return this.service.get(this.providerName, this.providerKey).pipe( tap((permissionRes: GetPermissionListResultDto) => { this.data = permissionRes; - this.selectedGroup = permissionRes.groups[0]; this.permissions = getPermissions(permissionRes.groups); + this.setSelectedGroup(permissionRes.groups[0]); this.disabledSelectAllInAllTabs = this.permissions.every( per => per.isGranted && diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/card/card-body.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/card/card-body.component.ts new file mode 100644 index 0000000000..b7efd90236 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/card/card-body.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'abp-card-body', + template: `
+ +
`, +}) +export class CardBodyComponent {} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/card/card-title.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/card/card-title.component.ts new file mode 100644 index 0000000000..f629636854 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/card/card-title.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'abp-card-title', + template: `
+ +
`, +}) +export class CardTitleComponent {} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/card/card.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/card/card.component.ts new file mode 100644 index 0000000000..317e5800b9 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/card/card.component.ts @@ -0,0 +1,22 @@ +import { Component, ContentChild, Input } from '@angular/core'; +import { CardBodyComponent } from './card-body.component'; +import { CardTitleComponent } from './card-title.component'; + +@Component({ + selector: 'abp-card', + template: `
+ + +
`, +}) +export class CardComponent { + @Input() cardClass: string; + + @Input() cardStyle: string; + + @ContentChild(CardBodyComponent) + cardBodyTemplate?: CardBodyComponent; + + @ContentChild(CardTitleComponent) + cardTitleTemplate?: CardTitleComponent; +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/card/card.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/card/card.module.ts new file mode 100644 index 0000000000..d8050a40cd --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/card/card.module.ts @@ -0,0 +1,14 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { CardBodyComponent } from './card-body.component'; +import { CardTitleComponent } from './card-title.component'; +import { CardComponent } from './card.component'; + +const declarationsWithExports = [CardComponent, CardBodyComponent, CardTitleComponent]; + +@NgModule({ + declarations: [...declarationsWithExports], + imports: [CommonModule], + exports: [...declarationsWithExports], +}) +export class CardModule {} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/card/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/card/index.ts new file mode 100644 index 0000000000..8d6bbd86d8 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/card/index.ts @@ -0,0 +1,4 @@ +export * from './card.module'; +export * from './card.component'; +export * from './card-body.component'; +export * from './card-title.component'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/checkbox/checkbox.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/checkbox/checkbox.component.ts new file mode 100644 index 0000000000..3ead2f929d --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/checkbox/checkbox.component.ts @@ -0,0 +1,45 @@ +import { AbstractNgModelComponent } from '@abp/ng.core'; +import { Component, EventEmitter, forwardRef, Injector, Input, Output } from '@angular/core'; +import { NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Component({ + selector: 'abp-checkbox', + template: ` +
+ + +
+ `, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => FormCheckboxComponent), + multi: true, + }, + ] +}) +export class FormCheckboxComponent extends AbstractNgModelComponent { + + @Input() label?: string; + @Input() labelClass = 'form-check-label'; + @Input() checkboxId!: string; + @Input() checkboxStyle = ''; + @Input() checkboxClass = 'form-check-input'; + @Input() checkboxReadonly = false; + @Output() onBlur = new EventEmitter(); + @Output() onFocus = new EventEmitter(); + + constructor(injector: Injector) { + super(injector); + } + +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/form-input/form-input.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/form-input/form-input.component.ts new file mode 100644 index 0000000000..d7f1f48c9d --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/form-input/form-input.component.ts @@ -0,0 +1,46 @@ +import { AbstractNgModelComponent } from '@abp/ng.core'; +import { Component, EventEmitter, forwardRef, Injector, Input, Output } from '@angular/core'; +import { NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Component({ + selector: 'abp-form-input', + template: ` +
+ + +
+ `, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => FormInputComponent), + multi: true, + }, + ] +}) +export class FormInputComponent extends AbstractNgModelComponent { + @Input() inputId!: string; + @Input() inputReadonly: boolean = false; + @Input() label: string = ''; + @Input() labelClass = 'form-label'; + @Input() inputPlaceholder: string = ''; + @Input() inputType: string = 'text'; + @Input() inputStyle: string = ''; + @Input() inputClass: string = 'form-control'; + @Output() onBlur = new EventEmitter(); + @Output() onFocus = new EventEmitter(); + + constructor(injector: Injector) { + super(injector); + } + +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/index.ts index 810d5a991a..238bc19589 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/index.ts @@ -11,3 +11,6 @@ export * from './modal/modal.component'; export * from './toast-container/toast-container.component'; export * from './toast/toast.component'; export * from './password/password.component'; +export * from './card/index'; +export * from './checkbox/checkbox.component'; +export * from './form-input/form-input.component'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/enums/form.ts b/npm/ng-packs/packages/theme-shared/src/lib/enums/form.ts new file mode 100644 index 0000000000..9ccbb93655 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/enums/form.ts @@ -0,0 +1,4 @@ +export enum eFormComponets { + FormInputComponent = 'FormInputComponent', + FormCheckboxComponent = 'FormCheckboxComponent', +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/enums/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/enums/index.ts index 3bda94b078..9e781067f7 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/enums/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/enums/index.ts @@ -1 +1,2 @@ +export * from './form'; export * from './route-names'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/checkbox.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/checkbox.component.spec.ts new file mode 100644 index 0000000000..e2fba6d0da --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/checkbox.component.spec.ts @@ -0,0 +1,44 @@ +import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest'; +import { FormCheckboxComponent } from '../components/checkbox/checkbox.component'; + +describe('FormCheckboxComponent', () => { + let spectator: SpectatorHost; + + const createHost = createHostFactory(FormCheckboxComponent); + + beforeEach( + () => + (spectator = createHost( + '', + { + hostProps: { attributes: { autofocus: '', name: 'abp-checkbox' } }, + }, + )), + ); + + it('should display the input', () => { + expect(spectator.query('input')).toBeTruthy(); + }); + + it('should equal the default classes to form-check-input', () => { + expect(spectator.query('input')).toHaveClass('form-check-input'); + }); + + it('should equal the default type to checkbox', () => { + expect(spectator.query('input')).toHaveAttribute('type', 'checkbox'); + }); + + it('should be readonly when checkboxReadonly is true', () => { + spectator.component.checkboxReadonly = true; + spectator.detectComponentChanges(); + expect(spectator.query('[readonly]')).toBeTruthy(); + }); + + it('should not contain readonly when checboxReadonly is false', () => { + spectator.component.checkboxReadonly = false; + spectator.detectComponentChanges(); + expect(spectator.query('[disabled]')).toBeFalsy(); + }); + +}); + diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/form-input.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/form-input.component.spec.ts new file mode 100644 index 0000000000..660cb72165 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/form-input.component.spec.ts @@ -0,0 +1,46 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest'; +import { FormInputComponent } from '../components/form-input/form-input.component'; + + +describe('FormInputComponent', () => { + let spectator: SpectatorHost; + + const createHost = createHostFactory(FormInputComponent); + + beforeEach( + () => + (spectator = createHost( + '', + { + hostProps: { attributes: { autofocus: '', name: 'abp-form-input' } }, + }, + )), + ); + + it('should display the input', () => { + expect(spectator.query('input')).toBeTruthy(); + }); + + it('should equal the default classes to form-control', () => { + expect(spectator.query('input')).toHaveClass('form-control'); + }); + + it('should equal the default type to text', () => { + expect(spectator.query('input')).toHaveAttribute('type', 'text'); + }); + + it('should be readonly when inputReadonly is true', () => { + spectator.component.inputReadonly = true; + spectator.detectComponentChanges(); + expect(spectator.query('[readonly]')).toBeTruthy(); + }); + + it('should not contain readonly when inputReadonly is false', () => { + spectator.component.inputReadonly = false; + spectator.detectComponentChanges(); + expect(spectator.query('[disabled]')).toBeFalsy(); + }); + +}); + diff --git a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts index 20c7de13d4..c7dad57f77 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts @@ -36,7 +36,10 @@ import { HTTP_ERROR_CONFIG, httpErrorConfigFactory } from './tokens/http-error.t import { DateParserFormatter } from './utils/date-parser-formatter'; import { CONFIRMATION_ICONS, DEFAULT_CONFIRMATION_ICONS } from './tokens/confirmation-icons.token'; import { PasswordComponent } from './components/password/password.component'; +import { CardModule } from './components/card/card.module'; import { AbpVisibleDirective } from './directives'; +import { FormInputComponent } from './components/form-input/form-input.component'; +import { FormCheckboxComponent } from './components/checkbox/checkbox.component'; const declarationsWithExports = [ BreadcrumbComponent, @@ -54,6 +57,8 @@ const declarationsWithExports = [ LoadingDirective, ModalCloseDirective, AbpVisibleDirective, + FormInputComponent, + FormCheckboxComponent ]; @NgModule({ @@ -63,12 +68,20 @@ const declarationsWithExports = [ NgxValidateCoreModule, NgbPaginationModule, EllipsisModule, + CardModule, + ], declarations: [...declarationsWithExports, HttpErrorWrapperComponent], - exports: [NgxDatatableModule, EllipsisModule, NgxValidateCoreModule, ...declarationsWithExports], + exports: [ + NgxDatatableModule, + EllipsisModule, + NgxValidateCoreModule, + ...declarationsWithExports, + CardModule, + ], providers: [DatePipe], }) -export class BaseThemeSharedModule {} +export class BaseThemeSharedModule { } @NgModule({ imports: [BaseThemeSharedModule], diff --git a/npm/ng-packs/tsconfig.base.json b/npm/ng-packs/tsconfig.base.json index ac29fd53ad..71e32f80ed 100644 --- a/npm/ng-packs/tsconfig.base.json +++ b/npm/ng-packs/tsconfig.base.json @@ -30,6 +30,7 @@ "@abp/ng.identity": ["packages/identity/src/public-api.ts"], "@abp/ng.identity/config": ["packages/identity/config/src/public-api.ts"], "@abp/ng.identity/proxy": ["packages/identity/proxy/src/public-api.ts"], + "@abp/ng.oauth": ["packages/oauth/src/public-api.ts"], "@abp/ng.permission-management": ["packages/permission-management/src/public-api.ts"], "@abp/ng.permission-management/proxy": [ "packages/permission-management/proxy/src/public-api.ts" @@ -43,8 +44,7 @@ "@abp/ng.theme.basic/testing": ["packages/theme-basic/testing/src/public-api.ts"], "@abp/ng.theme.shared": ["packages/theme-shared/src/public-api.ts"], "@abp/ng.theme.shared/extensions": ["packages/theme-shared/extensions/src/public-api.ts"], - "@abp/ng.theme.shared/testing": ["packages/theme-shared/testing/src/public-api.ts"], - "@abp/ng.oauth": ["packages/oauth/src/public-api.ts"] + "@abp/ng.theme.shared/testing": ["packages/theme-shared/testing/src/public-api.ts"] } }, "exclude": ["node_modules", "tmp"] diff --git a/npm/packs/flag-icon-css/abp.resourcemapping.js b/npm/packs/flag-icon-css/abp.resourcemapping.js deleted file mode 100644 index 8c3ae431dd..0000000000 --- a/npm/packs/flag-icon-css/abp.resourcemapping.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - mappings: { - "@node_modules/flag-icon-css/css/*": "@libs/flag-icon-css/css", - "@node_modules/flag-icon-css/flags/1x1/*": "@libs/flag-icon-css/flags/1x1" - } -} \ No newline at end of file diff --git a/npm/packs/flag-icons/abp.resourcemapping.js b/npm/packs/flag-icons/abp.resourcemapping.js new file mode 100644 index 0000000000..377a2aee6f --- /dev/null +++ b/npm/packs/flag-icons/abp.resourcemapping.js @@ -0,0 +1,6 @@ +module.exports = { + mappings: { + "@node_modules/flag-icons/css/*": "@libs/flag-icons/css", + "@node_modules/flag-icons/flags/1x1/*": "@libs/flag-icons/flags/1x1" + } +} \ No newline at end of file diff --git a/npm/packs/flag-icons/package.json b/npm/packs/flag-icons/package.json new file mode 100644 index 0000000000..d77c47197b --- /dev/null +++ b/npm/packs/flag-icons/package.json @@ -0,0 +1,11 @@ +{ + "version": "7.0.1", + "name": "@abp/flag-icons", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "flag-icons": "6.6.6" + }, + "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431" +} \ No newline at end of file diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20221220114435_Initial.Designer.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20230216095309_Initial.Designer.cs similarity index 99% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20221220114435_Initial.Designer.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20230216095309_Initial.Designer.cs index a1747ee5fa..61c01dafb3 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20221220114435_Initial.Designer.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20230216095309_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations { [DbContext(typeof(MyProjectNameDbContext))] - [Migration("20221220114435_Initial")] + [Migration("20230216095309_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -765,6 +765,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20221220114435_Initial.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20230216095309_Initial.cs similarity index 99% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20221220114435_Initial.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20230216095309_Initial.cs index 90d52b8c31..72b29f70ed 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20221220114435_Initial.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20230216095309_Initial.cs @@ -315,6 +315,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), AccessFailedCount = table.Column(type: "int", nullable: false, defaultValue: 0), + ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/MyProjectNameDbContextModelSnapshot.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/MyProjectNameDbContextModelSnapshot.cs index c881e44e52..c0b3f67565 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/MyProjectNameDbContextModelSnapshot.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/MyProjectNameDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -762,6 +762,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20221205080257_Initial.Designer.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20230216094727_Initial.Designer.cs similarity index 99% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20221205080257_Initial.Designer.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20230216094727_Initial.Designer.cs index 81cbbcf0d0..afc35017e1 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20221205080257_Initial.Designer.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20230216094727_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { [DbContext(typeof(MyProjectNameDbContext))] - [Migration("20221205080257_Initial")] + [Migration("20230216094727_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -497,6 +497,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -683,6 +686,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasDefaultValue(false) .HasColumnName("EmailConfirmed"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -759,6 +765,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") @@ -961,6 +970,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(128)") .HasColumnName("DisplayName"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -1539,6 +1551,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("datetime2") .HasColumnName("DeletionTime"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20221220114625_Initial.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20230216094727_Initial.cs similarity index 99% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20221220114625_Initial.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20230216094727_Initial.cs index 75e2c5889d..feed514678 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20221220114625_Initial.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20230216094727_Initial.cs @@ -315,6 +315,7 @@ namespace MyCompanyName.MyProjectName.Migrations LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), AccessFailedCount = table.Column(type: "int", nullable: false, defaultValue: 0), + ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/MyProjectNameDbContextModelSnapshot.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/MyProjectNameDbContextModelSnapshot.cs index d2becf58f5..81d2653b8b 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/MyProjectNameDbContextModelSnapshot.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/MyProjectNameDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -494,6 +494,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -680,6 +683,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasDefaultValue(false) .HasColumnName("EmailConfirmed"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -756,6 +762,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") @@ -958,6 +967,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(128)") .HasColumnName("DisplayName"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -1536,6 +1548,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("datetime2") .HasColumnName("DeletionTime"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20220913013918_Initial.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20220913013918_Initial.cs deleted file mode 100644 index d34c67552d..0000000000 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20220913013918_Initial.cs +++ /dev/null @@ -1,1027 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace MyCompanyName.MyProjectName.Host.Migrations -{ - public partial class Initial : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "AbpAuditLogs", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - ApplicationName = table.Column(type: "nvarchar(96)", maxLength: 96, nullable: true), - UserId = table.Column(type: "uniqueidentifier", nullable: true), - UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - TenantName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - ImpersonatorUserId = table.Column(type: "uniqueidentifier", nullable: true), - ImpersonatorUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - ImpersonatorTenantId = table.Column(type: "uniqueidentifier", nullable: true), - ImpersonatorTenantName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - ExecutionTime = table.Column(type: "datetime2", nullable: false), - ExecutionDuration = table.Column(type: "int", nullable: false), - ClientIpAddress = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - ClientName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), - ClientId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - CorrelationId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - BrowserInfo = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), - HttpMethod = table.Column(type: "nvarchar(16)", maxLength: 16, nullable: true), - Url = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - Exceptions = table.Column(type: "nvarchar(max)", nullable: true), - Comments = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - HttpStatusCode = table.Column(type: "int", nullable: true), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpAuditLogs", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpClaimTypes", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - Required = table.Column(type: "bit", nullable: false), - IsStatic = table.Column(type: "bit", nullable: false), - Regex = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), - RegexDescription = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), - Description = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - ValueType = table.Column(type: "int", nullable: false), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpClaimTypes", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpFeatureGroups", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpFeatureGroups", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpFeatures", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - ParentName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), - DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - Description = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - DefaultValue = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - IsVisibleToClients = table.Column(type: "bit", nullable: false), - IsAvailableToHost = table.Column(type: "bit", nullable: false), - AllowedProviders = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - ValueType = table.Column(type: "nvarchar(2048)", maxLength: 2048, nullable: true), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpFeatures", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpFeatureValues", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - Value = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpFeatureValues", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpLinkUsers", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - SourceUserId = table.Column(type: "uniqueidentifier", nullable: false), - SourceTenantId = table.Column(type: "uniqueidentifier", nullable: true), - TargetUserId = table.Column(type: "uniqueidentifier", nullable: false), - TargetTenantId = table.Column(type: "uniqueidentifier", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpLinkUsers", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpOrganizationUnits", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - ParentId = table.Column(type: "uniqueidentifier", nullable: true), - Code = table.Column(type: "nvarchar(95)", maxLength: 95, nullable: false), - DisplayName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpOrganizationUnits", x => x.Id); - table.ForeignKey( - name: "FK_AbpOrganizationUnits_AbpOrganizationUnits_ParentId", - column: x => x.ParentId, - principalTable: "AbpOrganizationUnits", - principalColumn: "Id"); - }); - - migrationBuilder.CreateTable( - name: "AbpPermissionGrants", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpPermissionGrants", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpPermissionGroups", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpPermissionGroups", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpPermissions", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - ParentName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), - DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - IsEnabled = table.Column(type: "bit", nullable: false), - MultiTenancySide = table.Column(type: "tinyint", nullable: false), - Providers = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), - StateCheckers = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpPermissions", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpRoles", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - IsDefault = table.Column(type: "bit", nullable: false), - IsStatic = table.Column(type: "bit", nullable: false), - IsPublic = table.Column(type: "bit", nullable: false), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpRoles", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpSecurityLogs", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - ApplicationName = table.Column(type: "nvarchar(96)", maxLength: 96, nullable: true), - Identity = table.Column(type: "nvarchar(96)", maxLength: 96, nullable: true), - Action = table.Column(type: "nvarchar(96)", maxLength: 96, nullable: true), - UserId = table.Column(type: "uniqueidentifier", nullable: true), - UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - TenantName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - ClientId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - CorrelationId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - ClientIpAddress = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - BrowserInfo = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpSecurityLogs", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpSettings", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - Value = table.Column(type: "nvarchar(2048)", maxLength: 2048, nullable: false), - ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpSettings", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpTenants", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpTenants", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpUsers", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - Name = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - Surname = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - EmailConfirmed = table.Column(type: "bit", nullable: false, defaultValue: false), - PasswordHash = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - SecurityStamp = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - IsExternal = table.Column(type: "bit", nullable: false, defaultValue: false), - PhoneNumber = table.Column(type: "nvarchar(16)", maxLength: 16, nullable: true), - PhoneNumberConfirmed = table.Column(type: "bit", nullable: false, defaultValue: false), - IsActive = table.Column(type: "bit", nullable: false), - TwoFactorEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), - LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), - LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), - AccessFailedCount = table.Column(type: "int", nullable: false, defaultValue: 0), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUsers", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "OpenIddictApplications", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - ClientId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), - ClientSecret = table.Column(type: "nvarchar(max)", nullable: true), - ConsentType = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), - DisplayName = table.Column(type: "nvarchar(max)", nullable: true), - DisplayNames = table.Column(type: "nvarchar(max)", nullable: true), - Permissions = table.Column(type: "nvarchar(max)", nullable: true), - PostLogoutRedirectUris = table.Column(type: "nvarchar(max)", nullable: true), - Properties = table.Column(type: "nvarchar(max)", nullable: true), - RedirectUris = table.Column(type: "nvarchar(max)", nullable: true), - Requirements = table.Column(type: "nvarchar(max)", nullable: true), - Type = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), - ClientUri = table.Column(type: "nvarchar(max)", nullable: true), - LogoUri = table.Column(type: "nvarchar(max)", nullable: true), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_OpenIddictApplications", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "OpenIddictScopes", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Description = table.Column(type: "nvarchar(max)", nullable: true), - Descriptions = table.Column(type: "nvarchar(max)", nullable: true), - DisplayName = table.Column(type: "nvarchar(max)", nullable: true), - DisplayNames = table.Column(type: "nvarchar(max)", nullable: true), - Name = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), - Properties = table.Column(type: "nvarchar(max)", nullable: true), - Resources = table.Column(type: "nvarchar(max)", nullable: true), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_OpenIddictScopes", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpAuditLogActions", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - AuditLogId = table.Column(type: "uniqueidentifier", nullable: false), - ServiceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - MethodName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), - Parameters = table.Column(type: "nvarchar(2000)", maxLength: 2000, nullable: true), - ExecutionTime = table.Column(type: "datetime2", nullable: false), - ExecutionDuration = table.Column(type: "int", nullable: false), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpAuditLogActions", x => x.Id); - table.ForeignKey( - name: "FK_AbpAuditLogActions_AbpAuditLogs_AuditLogId", - column: x => x.AuditLogId, - principalTable: "AbpAuditLogs", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpEntityChanges", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - AuditLogId = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - ChangeTime = table.Column(type: "datetime2", nullable: false), - ChangeType = table.Column(type: "tinyint", nullable: false), - EntityTenantId = table.Column(type: "uniqueidentifier", nullable: true), - EntityId = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - EntityTypeFullName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpEntityChanges", x => x.Id); - table.ForeignKey( - name: "FK_AbpEntityChanges_AbpAuditLogs_AuditLogId", - column: x => x.AuditLogId, - principalTable: "AbpAuditLogs", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpOrganizationUnitRoles", - columns: table => new - { - RoleId = table.Column(type: "uniqueidentifier", nullable: false), - OrganizationUnitId = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpOrganizationUnitRoles", x => new { x.OrganizationUnitId, x.RoleId }); - table.ForeignKey( - name: "FK_AbpOrganizationUnitRoles_AbpOrganizationUnits_OrganizationUnitId", - column: x => x.OrganizationUnitId, - principalTable: "AbpOrganizationUnits", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_AbpOrganizationUnitRoles_AbpRoles_RoleId", - column: x => x.RoleId, - principalTable: "AbpRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpRoleClaims", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - RoleId = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - ClaimType = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - ClaimValue = table.Column(type: "nvarchar(1024)", maxLength: 1024, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpRoleClaims", x => x.Id); - table.ForeignKey( - name: "FK_AbpRoleClaims_AbpRoles_RoleId", - column: x => x.RoleId, - principalTable: "AbpRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpTenantConnectionStrings", - columns: table => new - { - TenantId = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - Value = table.Column(type: "nvarchar(1024)", maxLength: 1024, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpTenantConnectionStrings", x => new { x.TenantId, x.Name }); - table.ForeignKey( - name: "FK_AbpTenantConnectionStrings_AbpTenants_TenantId", - column: x => x.TenantId, - principalTable: "AbpTenants", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpUserClaims", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - UserId = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - ClaimType = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - ClaimValue = table.Column(type: "nvarchar(1024)", maxLength: 1024, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUserClaims", x => x.Id); - table.ForeignKey( - name: "FK_AbpUserClaims_AbpUsers_UserId", - column: x => x.UserId, - principalTable: "AbpUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpUserLogins", - columns: table => new - { - UserId = table.Column(type: "uniqueidentifier", nullable: false), - LoginProvider = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - ProviderKey = table.Column(type: "nvarchar(196)", maxLength: 196, nullable: false), - ProviderDisplayName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUserLogins", x => new { x.UserId, x.LoginProvider }); - table.ForeignKey( - name: "FK_AbpUserLogins_AbpUsers_UserId", - column: x => x.UserId, - principalTable: "AbpUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpUserOrganizationUnits", - columns: table => new - { - UserId = table.Column(type: "uniqueidentifier", nullable: false), - OrganizationUnitId = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUserOrganizationUnits", x => new { x.OrganizationUnitId, x.UserId }); - table.ForeignKey( - name: "FK_AbpUserOrganizationUnits_AbpOrganizationUnits_OrganizationUnitId", - column: x => x.OrganizationUnitId, - principalTable: "AbpOrganizationUnits", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_AbpUserOrganizationUnits_AbpUsers_UserId", - column: x => x.UserId, - principalTable: "AbpUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpUserRoles", - columns: table => new - { - UserId = table.Column(type: "uniqueidentifier", nullable: false), - RoleId = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUserRoles", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK_AbpUserRoles_AbpRoles_RoleId", - column: x => x.RoleId, - principalTable: "AbpRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_AbpUserRoles_AbpUsers_UserId", - column: x => x.UserId, - principalTable: "AbpUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpUserTokens", - columns: table => new - { - UserId = table.Column(type: "uniqueidentifier", nullable: false), - LoginProvider = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), - Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - Value = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK_AbpUserTokens_AbpUsers_UserId", - column: x => x.UserId, - principalTable: "AbpUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "OpenIddictAuthorizations", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - ApplicationId = table.Column(type: "uniqueidentifier", nullable: true), - CreationDate = table.Column(type: "datetime2", nullable: true), - Properties = table.Column(type: "nvarchar(max)", nullable: true), - Scopes = table.Column(type: "nvarchar(max)", nullable: true), - Status = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), - Subject = table.Column(type: "nvarchar(400)", maxLength: 400, nullable: true), - Type = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id); - table.ForeignKey( - name: "FK_OpenIddictAuthorizations_OpenIddictApplications_ApplicationId", - column: x => x.ApplicationId, - principalTable: "OpenIddictApplications", - principalColumn: "Id"); - }); - - migrationBuilder.CreateTable( - name: "AbpEntityPropertyChanges", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - EntityChangeId = table.Column(type: "uniqueidentifier", nullable: false), - NewValue = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), - OriginalValue = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), - PropertyName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - PropertyTypeFullName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpEntityPropertyChanges", x => x.Id); - table.ForeignKey( - name: "FK_AbpEntityPropertyChanges_AbpEntityChanges_EntityChangeId", - column: x => x.EntityChangeId, - principalTable: "AbpEntityChanges", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "OpenIddictTokens", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - ApplicationId = table.Column(type: "uniqueidentifier", nullable: true), - AuthorizationId = table.Column(type: "uniqueidentifier", nullable: true), - CreationDate = table.Column(type: "datetime2", nullable: true), - ExpirationDate = table.Column(type: "datetime2", nullable: true), - Payload = table.Column(type: "nvarchar(max)", nullable: true), - Properties = table.Column(type: "nvarchar(max)", nullable: true), - RedemptionDate = table.Column(type: "datetime2", nullable: true), - ReferenceId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), - Status = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), - Subject = table.Column(type: "nvarchar(400)", maxLength: 400, nullable: true), - Type = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_OpenIddictTokens", x => x.Id); - table.ForeignKey( - name: "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId", - column: x => x.ApplicationId, - principalTable: "OpenIddictApplications", - principalColumn: "Id"); - table.ForeignKey( - name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId", - column: x => x.AuthorizationId, - principalTable: "OpenIddictAuthorizations", - principalColumn: "Id"); - }); - - migrationBuilder.CreateIndex( - name: "IX_AbpAuditLogActions_AuditLogId", - table: "AbpAuditLogActions", - column: "AuditLogId"); - - migrationBuilder.CreateIndex( - name: "IX_AbpAuditLogActions_TenantId_ServiceName_MethodName_ExecutionTime", - table: "AbpAuditLogActions", - columns: new[] { "TenantId", "ServiceName", "MethodName", "ExecutionTime" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpAuditLogs_TenantId_ExecutionTime", - table: "AbpAuditLogs", - columns: new[] { "TenantId", "ExecutionTime" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpAuditLogs_TenantId_UserId_ExecutionTime", - table: "AbpAuditLogs", - columns: new[] { "TenantId", "UserId", "ExecutionTime" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpEntityChanges_AuditLogId", - table: "AbpEntityChanges", - column: "AuditLogId"); - - migrationBuilder.CreateIndex( - name: "IX_AbpEntityChanges_TenantId_EntityTypeFullName_EntityId", - table: "AbpEntityChanges", - columns: new[] { "TenantId", "EntityTypeFullName", "EntityId" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpEntityPropertyChanges_EntityChangeId", - table: "AbpEntityPropertyChanges", - column: "EntityChangeId"); - - migrationBuilder.CreateIndex( - name: "IX_AbpFeatureGroups_Name", - table: "AbpFeatureGroups", - column: "Name", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_AbpFeatures_GroupName", - table: "AbpFeatures", - column: "GroupName"); - - migrationBuilder.CreateIndex( - name: "IX_AbpFeatures_Name", - table: "AbpFeatures", - column: "Name", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey", - table: "AbpFeatureValues", - columns: new[] { "Name", "ProviderName", "ProviderKey" }, - unique: true, - filter: "[ProviderName] IS NOT NULL AND [ProviderKey] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX_AbpLinkUsers_SourceUserId_SourceTenantId_TargetUserId_TargetTenantId", - table: "AbpLinkUsers", - columns: new[] { "SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId" }, - unique: true, - filter: "[SourceTenantId] IS NOT NULL AND [TargetTenantId] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX_AbpOrganizationUnitRoles_RoleId_OrganizationUnitId", - table: "AbpOrganizationUnitRoles", - columns: new[] { "RoleId", "OrganizationUnitId" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpOrganizationUnits_Code", - table: "AbpOrganizationUnits", - column: "Code"); - - migrationBuilder.CreateIndex( - name: "IX_AbpOrganizationUnits_ParentId", - table: "AbpOrganizationUnits", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX_AbpPermissionGrants_TenantId_Name_ProviderName_ProviderKey", - table: "AbpPermissionGrants", - columns: new[] { "TenantId", "Name", "ProviderName", "ProviderKey" }, - unique: true, - filter: "[TenantId] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX_AbpPermissionGroups_Name", - table: "AbpPermissionGroups", - column: "Name", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_AbpPermissions_GroupName", - table: "AbpPermissions", - column: "GroupName"); - - migrationBuilder.CreateIndex( - name: "IX_AbpPermissions_Name", - table: "AbpPermissions", - column: "Name", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_AbpRoleClaims_RoleId", - table: "AbpRoleClaims", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_AbpRoles_NormalizedName", - table: "AbpRoles", - column: "NormalizedName"); - - migrationBuilder.CreateIndex( - name: "IX_AbpSecurityLogs_TenantId_Action", - table: "AbpSecurityLogs", - columns: new[] { "TenantId", "Action" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpSecurityLogs_TenantId_ApplicationName", - table: "AbpSecurityLogs", - columns: new[] { "TenantId", "ApplicationName" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpSecurityLogs_TenantId_Identity", - table: "AbpSecurityLogs", - columns: new[] { "TenantId", "Identity" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpSecurityLogs_TenantId_UserId", - table: "AbpSecurityLogs", - columns: new[] { "TenantId", "UserId" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpSettings_Name_ProviderName_ProviderKey", - table: "AbpSettings", - columns: new[] { "Name", "ProviderName", "ProviderKey" }, - unique: true, - filter: "[ProviderName] IS NOT NULL AND [ProviderKey] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX_AbpTenants_Name", - table: "AbpTenants", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_AbpUserClaims_UserId", - table: "AbpUserClaims", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AbpUserLogins_LoginProvider_ProviderKey", - table: "AbpUserLogins", - columns: new[] { "LoginProvider", "ProviderKey" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpUserOrganizationUnits_UserId_OrganizationUnitId", - table: "AbpUserOrganizationUnits", - columns: new[] { "UserId", "OrganizationUnitId" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpUserRoles_RoleId_UserId", - table: "AbpUserRoles", - columns: new[] { "RoleId", "UserId" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpUsers_Email", - table: "AbpUsers", - column: "Email"); - - migrationBuilder.CreateIndex( - name: "IX_AbpUsers_NormalizedEmail", - table: "AbpUsers", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX_AbpUsers_NormalizedUserName", - table: "AbpUsers", - column: "NormalizedUserName"); - - migrationBuilder.CreateIndex( - name: "IX_AbpUsers_UserName", - table: "AbpUsers", - column: "UserName"); - - migrationBuilder.CreateIndex( - name: "IX_OpenIddictApplications_ClientId", - table: "OpenIddictApplications", - column: "ClientId"); - - migrationBuilder.CreateIndex( - name: "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type", - table: "OpenIddictAuthorizations", - columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); - - migrationBuilder.CreateIndex( - name: "IX_OpenIddictScopes_Name", - table: "OpenIddictScopes", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type", - table: "OpenIddictTokens", - columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); - - migrationBuilder.CreateIndex( - name: "IX_OpenIddictTokens_AuthorizationId", - table: "OpenIddictTokens", - column: "AuthorizationId"); - - migrationBuilder.CreateIndex( - name: "IX_OpenIddictTokens_ReferenceId", - table: "OpenIddictTokens", - column: "ReferenceId"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AbpAuditLogActions"); - - migrationBuilder.DropTable( - name: "AbpClaimTypes"); - - migrationBuilder.DropTable( - name: "AbpEntityPropertyChanges"); - - migrationBuilder.DropTable( - name: "AbpFeatureGroups"); - - migrationBuilder.DropTable( - name: "AbpFeatures"); - - migrationBuilder.DropTable( - name: "AbpFeatureValues"); - - migrationBuilder.DropTable( - name: "AbpLinkUsers"); - - migrationBuilder.DropTable( - name: "AbpOrganizationUnitRoles"); - - migrationBuilder.DropTable( - name: "AbpPermissionGrants"); - - migrationBuilder.DropTable( - name: "AbpPermissionGroups"); - - migrationBuilder.DropTable( - name: "AbpPermissions"); - - migrationBuilder.DropTable( - name: "AbpRoleClaims"); - - migrationBuilder.DropTable( - name: "AbpSecurityLogs"); - - migrationBuilder.DropTable( - name: "AbpSettings"); - - migrationBuilder.DropTable( - name: "AbpTenantConnectionStrings"); - - migrationBuilder.DropTable( - name: "AbpUserClaims"); - - migrationBuilder.DropTable( - name: "AbpUserLogins"); - - migrationBuilder.DropTable( - name: "AbpUserOrganizationUnits"); - - migrationBuilder.DropTable( - name: "AbpUserRoles"); - - migrationBuilder.DropTable( - name: "AbpUserTokens"); - - migrationBuilder.DropTable( - name: "OpenIddictScopes"); - - migrationBuilder.DropTable( - name: "OpenIddictTokens"); - - migrationBuilder.DropTable( - name: "AbpEntityChanges"); - - migrationBuilder.DropTable( - name: "AbpTenants"); - - migrationBuilder.DropTable( - name: "AbpOrganizationUnits"); - - migrationBuilder.DropTable( - name: "AbpRoles"); - - migrationBuilder.DropTable( - name: "AbpUsers"); - - migrationBuilder.DropTable( - name: "OpenIddictAuthorizations"); - - migrationBuilder.DropTable( - name: "AbpAuditLogs"); - - migrationBuilder.DropTable( - name: "OpenIddictApplications"); - } - } -} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20220913013918_Initial.Designer.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20230216095619_Initial.Designer.cs similarity index 99% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20220913013918_Initial.Designer.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20230216095619_Initial.Designer.cs index bc2a256ed3..8d2e420bd4 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20220913013918_Initial.Designer.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20230216095619_Initial.Designer.cs @@ -13,18 +13,19 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Host.Migrations { [DbContext(typeof(MyProjectNameDbContext))] - [Migration("20220913013918_Initial")] + [Migration("20230216095619_Initial")] partial class Initial { + /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "6.0.5") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => { @@ -496,6 +497,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -682,6 +686,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasDefaultValue(false) .HasColumnName("EmailConfirmed"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -758,6 +765,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") @@ -960,6 +970,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasColumnType("nvarchar(128)") .HasColumnName("DisplayName"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -1538,6 +1551,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasColumnType("datetime2") .HasColumnName("DeletionTime"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20230216095619_Initial.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20230216095619_Initial.cs new file mode 100644 index 0000000000..52e886b7ef --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20230216095619_Initial.cs @@ -0,0 +1,1035 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MyCompanyName.MyProjectName.Host.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AbpAuditLogs", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ApplicationName = table.Column(type: "nvarchar(96)", maxLength: 96, nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: true), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + TenantName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + ImpersonatorUserId = table.Column(type: "uniqueidentifier", nullable: true), + ImpersonatorUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ImpersonatorTenantId = table.Column(type: "uniqueidentifier", nullable: true), + ImpersonatorTenantName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + ExecutionTime = table.Column(type: "datetime2", nullable: false), + ExecutionDuration = table.Column(type: "int", nullable: false), + ClientIpAddress = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + ClientName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + ClientId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + CorrelationId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + BrowserInfo = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), + HttpMethod = table.Column(type: "nvarchar(16)", maxLength: 16, nullable: true), + Url = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Exceptions = table.Column(type: "nvarchar(max)", nullable: true), + Comments = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + HttpStatusCode = table.Column(type: "int", nullable: true), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAuditLogs", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpClaimTypes", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Required = table.Column(type: "bit", nullable: false), + IsStatic = table.Column(type: "bit", nullable: false), + Regex = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), + RegexDescription = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + Description = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ValueType = table.Column(type: "int", nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpClaimTypes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpFeatureGroups", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpFeatureGroups", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpFeatures", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ParentName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Description = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + DefaultValue = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + IsVisibleToClients = table.Column(type: "bit", nullable: false), + IsAvailableToHost = table.Column(type: "bit", nullable: false), + AllowedProviders = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ValueType = table.Column(type: "nvarchar(2048)", maxLength: 2048, nullable: true), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpFeatures", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpFeatureValues", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Value = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpFeatureValues", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpLinkUsers", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + SourceUserId = table.Column(type: "uniqueidentifier", nullable: false), + SourceTenantId = table.Column(type: "uniqueidentifier", nullable: true), + TargetUserId = table.Column(type: "uniqueidentifier", nullable: false), + TargetTenantId = table.Column(type: "uniqueidentifier", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpLinkUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpOrganizationUnits", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + ParentId = table.Column(type: "uniqueidentifier", nullable: true), + Code = table.Column(type: "nvarchar(95)", maxLength: 95, nullable: false), + DisplayName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + EntityVersion = table.Column(type: "int", nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpOrganizationUnits", x => x.Id); + table.ForeignKey( + name: "FK_AbpOrganizationUnits_AbpOrganizationUnits_ParentId", + column: x => x.ParentId, + principalTable: "AbpOrganizationUnits", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "AbpPermissionGrants", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpPermissionGrants", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpPermissionGroups", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpPermissionGroups", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpPermissions", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ParentName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + IsEnabled = table.Column(type: "bit", nullable: false), + MultiTenancySide = table.Column(type: "tinyint", nullable: false), + Providers = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + StateCheckers = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpPermissions", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpRoles", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + IsDefault = table.Column(type: "bit", nullable: false), + IsStatic = table.Column(type: "bit", nullable: false), + IsPublic = table.Column(type: "bit", nullable: false), + EntityVersion = table.Column(type: "int", nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpSecurityLogs", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + ApplicationName = table.Column(type: "nvarchar(96)", maxLength: 96, nullable: true), + Identity = table.Column(type: "nvarchar(96)", maxLength: 96, nullable: true), + Action = table.Column(type: "nvarchar(96)", maxLength: 96, nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: true), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + TenantName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + ClientId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + CorrelationId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + ClientIpAddress = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + BrowserInfo = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpSecurityLogs", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpSettings", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Value = table.Column(type: "nvarchar(2048)", maxLength: 2048, nullable: false), + ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpSettings", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpTenants", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + EntityVersion = table.Column(type: "int", nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpTenants", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpUsers", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Name = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + Surname = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + EmailConfirmed = table.Column(type: "bit", nullable: false, defaultValue: false), + PasswordHash = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + SecurityStamp = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + IsExternal = table.Column(type: "bit", nullable: false, defaultValue: false), + PhoneNumber = table.Column(type: "nvarchar(16)", maxLength: 16, nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false, defaultValue: false), + IsActive = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), + AccessFailedCount = table.Column(type: "int", nullable: false, defaultValue: 0), + ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), + EntityVersion = table.Column(type: "int", nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictApplications", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ClientId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + ClientSecret = table.Column(type: "nvarchar(max)", nullable: true), + ConsentType = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + DisplayName = table.Column(type: "nvarchar(max)", nullable: true), + DisplayNames = table.Column(type: "nvarchar(max)", nullable: true), + Permissions = table.Column(type: "nvarchar(max)", nullable: true), + PostLogoutRedirectUris = table.Column(type: "nvarchar(max)", nullable: true), + Properties = table.Column(type: "nvarchar(max)", nullable: true), + RedirectUris = table.Column(type: "nvarchar(max)", nullable: true), + Requirements = table.Column(type: "nvarchar(max)", nullable: true), + Type = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + ClientUri = table.Column(type: "nvarchar(max)", nullable: true), + LogoUri = table.Column(type: "nvarchar(max)", nullable: true), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictApplications", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictScopes", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Description = table.Column(type: "nvarchar(max)", nullable: true), + Descriptions = table.Column(type: "nvarchar(max)", nullable: true), + DisplayName = table.Column(type: "nvarchar(max)", nullable: true), + DisplayNames = table.Column(type: "nvarchar(max)", nullable: true), + Name = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + Properties = table.Column(type: "nvarchar(max)", nullable: true), + Resources = table.Column(type: "nvarchar(max)", nullable: true), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictScopes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpAuditLogActions", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + AuditLogId = table.Column(type: "uniqueidentifier", nullable: false), + ServiceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + MethodName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + Parameters = table.Column(type: "nvarchar(2000)", maxLength: 2000, nullable: true), + ExecutionTime = table.Column(type: "datetime2", nullable: false), + ExecutionDuration = table.Column(type: "int", nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAuditLogActions", x => x.Id); + table.ForeignKey( + name: "FK_AbpAuditLogActions_AbpAuditLogs_AuditLogId", + column: x => x.AuditLogId, + principalTable: "AbpAuditLogs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpEntityChanges", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + AuditLogId = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + ChangeTime = table.Column(type: "datetime2", nullable: false), + ChangeType = table.Column(type: "tinyint", nullable: false), + EntityTenantId = table.Column(type: "uniqueidentifier", nullable: true), + EntityId = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + EntityTypeFullName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpEntityChanges", x => x.Id); + table.ForeignKey( + name: "FK_AbpEntityChanges_AbpAuditLogs_AuditLogId", + column: x => x.AuditLogId, + principalTable: "AbpAuditLogs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpOrganizationUnitRoles", + columns: table => new + { + RoleId = table.Column(type: "uniqueidentifier", nullable: false), + OrganizationUnitId = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpOrganizationUnitRoles", x => new { x.OrganizationUnitId, x.RoleId }); + table.ForeignKey( + name: "FK_AbpOrganizationUnitRoles_AbpOrganizationUnits_OrganizationUnitId", + column: x => x.OrganizationUnitId, + principalTable: "AbpOrganizationUnits", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AbpOrganizationUnitRoles_AbpRoles_RoleId", + column: x => x.RoleId, + principalTable: "AbpRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpRoleClaims", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + RoleId = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + ClaimType = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ClaimValue = table.Column(type: "nvarchar(1024)", maxLength: 1024, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AbpRoleClaims_AbpRoles_RoleId", + column: x => x.RoleId, + principalTable: "AbpRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpTenantConnectionStrings", + columns: table => new + { + TenantId = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + Value = table.Column(type: "nvarchar(1024)", maxLength: 1024, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpTenantConnectionStrings", x => new { x.TenantId, x.Name }); + table.ForeignKey( + name: "FK_AbpTenantConnectionStrings_AbpTenants_TenantId", + column: x => x.TenantId, + principalTable: "AbpTenants", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserClaims", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + ClaimType = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ClaimValue = table.Column(type: "nvarchar(1024)", maxLength: 1024, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AbpUserClaims_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserLogins", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + LoginProvider = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + ProviderKey = table.Column(type: "nvarchar(196)", maxLength: 196, nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserLogins", x => new { x.UserId, x.LoginProvider }); + table.ForeignKey( + name: "FK_AbpUserLogins_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserOrganizationUnits", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + OrganizationUnitId = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserOrganizationUnits", x => new { x.OrganizationUnitId, x.UserId }); + table.ForeignKey( + name: "FK_AbpUserOrganizationUnits_AbpOrganizationUnits_OrganizationUnitId", + column: x => x.OrganizationUnitId, + principalTable: "AbpOrganizationUnits", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AbpUserOrganizationUnits_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserRoles", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + RoleId = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AbpUserRoles_AbpRoles_RoleId", + column: x => x.RoleId, + principalTable: "AbpRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AbpUserRoles_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserTokens", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + LoginProvider = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Value = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AbpUserTokens_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictAuthorizations", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ApplicationId = table.Column(type: "uniqueidentifier", nullable: true), + CreationDate = table.Column(type: "datetime2", nullable: true), + Properties = table.Column(type: "nvarchar(max)", nullable: true), + Scopes = table.Column(type: "nvarchar(max)", nullable: true), + Status = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + Subject = table.Column(type: "nvarchar(400)", maxLength: 400, nullable: true), + Type = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id); + table.ForeignKey( + name: "FK_OpenIddictAuthorizations_OpenIddictApplications_ApplicationId", + column: x => x.ApplicationId, + principalTable: "OpenIddictApplications", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "AbpEntityPropertyChanges", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + EntityChangeId = table.Column(type: "uniqueidentifier", nullable: false), + NewValue = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), + OriginalValue = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), + PropertyName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + PropertyTypeFullName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpEntityPropertyChanges", x => x.Id); + table.ForeignKey( + name: "FK_AbpEntityPropertyChanges_AbpEntityChanges_EntityChangeId", + column: x => x.EntityChangeId, + principalTable: "AbpEntityChanges", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictTokens", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ApplicationId = table.Column(type: "uniqueidentifier", nullable: true), + AuthorizationId = table.Column(type: "uniqueidentifier", nullable: true), + CreationDate = table.Column(type: "datetime2", nullable: true), + ExpirationDate = table.Column(type: "datetime2", nullable: true), + Payload = table.Column(type: "nvarchar(max)", nullable: true), + Properties = table.Column(type: "nvarchar(max)", nullable: true), + RedemptionDate = table.Column(type: "datetime2", nullable: true), + ReferenceId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + Status = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + Subject = table.Column(type: "nvarchar(400)", maxLength: 400, nullable: true), + Type = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictTokens", x => x.Id); + table.ForeignKey( + name: "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId", + column: x => x.ApplicationId, + principalTable: "OpenIddictApplications", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId", + column: x => x.AuthorizationId, + principalTable: "OpenIddictAuthorizations", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogActions_AuditLogId", + table: "AbpAuditLogActions", + column: "AuditLogId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogActions_TenantId_ServiceName_MethodName_ExecutionTime", + table: "AbpAuditLogActions", + columns: new[] { "TenantId", "ServiceName", "MethodName", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogs_TenantId_ExecutionTime", + table: "AbpAuditLogs", + columns: new[] { "TenantId", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogs_TenantId_UserId_ExecutionTime", + table: "AbpAuditLogs", + columns: new[] { "TenantId", "UserId", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityChanges_AuditLogId", + table: "AbpEntityChanges", + column: "AuditLogId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityChanges_TenantId_EntityTypeFullName_EntityId", + table: "AbpEntityChanges", + columns: new[] { "TenantId", "EntityTypeFullName", "EntityId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityPropertyChanges_EntityChangeId", + table: "AbpEntityPropertyChanges", + column: "EntityChangeId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpFeatureGroups_Name", + table: "AbpFeatureGroups", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AbpFeatures_GroupName", + table: "AbpFeatures", + column: "GroupName"); + + migrationBuilder.CreateIndex( + name: "IX_AbpFeatures_Name", + table: "AbpFeatures", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey", + table: "AbpFeatureValues", + columns: new[] { "Name", "ProviderName", "ProviderKey" }, + unique: true, + filter: "[ProviderName] IS NOT NULL AND [ProviderKey] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AbpLinkUsers_SourceUserId_SourceTenantId_TargetUserId_TargetTenantId", + table: "AbpLinkUsers", + columns: new[] { "SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId" }, + unique: true, + filter: "[SourceTenantId] IS NOT NULL AND [TargetTenantId] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AbpOrganizationUnitRoles_RoleId_OrganizationUnitId", + table: "AbpOrganizationUnitRoles", + columns: new[] { "RoleId", "OrganizationUnitId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpOrganizationUnits_Code", + table: "AbpOrganizationUnits", + column: "Code"); + + migrationBuilder.CreateIndex( + name: "IX_AbpOrganizationUnits_ParentId", + table: "AbpOrganizationUnits", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpPermissionGrants_TenantId_Name_ProviderName_ProviderKey", + table: "AbpPermissionGrants", + columns: new[] { "TenantId", "Name", "ProviderName", "ProviderKey" }, + unique: true, + filter: "[TenantId] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AbpPermissionGroups_Name", + table: "AbpPermissionGroups", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AbpPermissions_GroupName", + table: "AbpPermissions", + column: "GroupName"); + + migrationBuilder.CreateIndex( + name: "IX_AbpPermissions_Name", + table: "AbpPermissions", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AbpRoleClaims_RoleId", + table: "AbpRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpRoles_NormalizedName", + table: "AbpRoles", + column: "NormalizedName"); + + migrationBuilder.CreateIndex( + name: "IX_AbpSecurityLogs_TenantId_Action", + table: "AbpSecurityLogs", + columns: new[] { "TenantId", "Action" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpSecurityLogs_TenantId_ApplicationName", + table: "AbpSecurityLogs", + columns: new[] { "TenantId", "ApplicationName" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpSecurityLogs_TenantId_Identity", + table: "AbpSecurityLogs", + columns: new[] { "TenantId", "Identity" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpSecurityLogs_TenantId_UserId", + table: "AbpSecurityLogs", + columns: new[] { "TenantId", "UserId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpSettings_Name_ProviderName_ProviderKey", + table: "AbpSettings", + columns: new[] { "Name", "ProviderName", "ProviderKey" }, + unique: true, + filter: "[ProviderName] IS NOT NULL AND [ProviderKey] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AbpTenants_Name", + table: "AbpTenants", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserClaims_UserId", + table: "AbpUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserLogins_LoginProvider_ProviderKey", + table: "AbpUserLogins", + columns: new[] { "LoginProvider", "ProviderKey" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserOrganizationUnits_UserId_OrganizationUnitId", + table: "AbpUserOrganizationUnits", + columns: new[] { "UserId", "OrganizationUnitId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserRoles_RoleId_UserId", + table: "AbpUserRoles", + columns: new[] { "RoleId", "UserId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_Email", + table: "AbpUsers", + column: "Email"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_NormalizedEmail", + table: "AbpUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_NormalizedUserName", + table: "AbpUsers", + column: "NormalizedUserName"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_UserName", + table: "AbpUsers", + column: "UserName"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictApplications_ClientId", + table: "OpenIddictApplications", + column: "ClientId"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type", + table: "OpenIddictAuthorizations", + columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictScopes_Name", + table: "OpenIddictScopes", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type", + table: "OpenIddictTokens", + columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_AuthorizationId", + table: "OpenIddictTokens", + column: "AuthorizationId"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ReferenceId", + table: "OpenIddictTokens", + column: "ReferenceId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AbpAuditLogActions"); + + migrationBuilder.DropTable( + name: "AbpClaimTypes"); + + migrationBuilder.DropTable( + name: "AbpEntityPropertyChanges"); + + migrationBuilder.DropTable( + name: "AbpFeatureGroups"); + + migrationBuilder.DropTable( + name: "AbpFeatures"); + + migrationBuilder.DropTable( + name: "AbpFeatureValues"); + + migrationBuilder.DropTable( + name: "AbpLinkUsers"); + + migrationBuilder.DropTable( + name: "AbpOrganizationUnitRoles"); + + migrationBuilder.DropTable( + name: "AbpPermissionGrants"); + + migrationBuilder.DropTable( + name: "AbpPermissionGroups"); + + migrationBuilder.DropTable( + name: "AbpPermissions"); + + migrationBuilder.DropTable( + name: "AbpRoleClaims"); + + migrationBuilder.DropTable( + name: "AbpSecurityLogs"); + + migrationBuilder.DropTable( + name: "AbpSettings"); + + migrationBuilder.DropTable( + name: "AbpTenantConnectionStrings"); + + migrationBuilder.DropTable( + name: "AbpUserClaims"); + + migrationBuilder.DropTable( + name: "AbpUserLogins"); + + migrationBuilder.DropTable( + name: "AbpUserOrganizationUnits"); + + migrationBuilder.DropTable( + name: "AbpUserRoles"); + + migrationBuilder.DropTable( + name: "AbpUserTokens"); + + migrationBuilder.DropTable( + name: "OpenIddictScopes"); + + migrationBuilder.DropTable( + name: "OpenIddictTokens"); + + migrationBuilder.DropTable( + name: "AbpEntityChanges"); + + migrationBuilder.DropTable( + name: "AbpTenants"); + + migrationBuilder.DropTable( + name: "AbpOrganizationUnits"); + + migrationBuilder.DropTable( + name: "AbpRoles"); + + migrationBuilder.DropTable( + name: "AbpUsers"); + + migrationBuilder.DropTable( + name: "OpenIddictAuthorizations"); + + migrationBuilder.DropTable( + name: "AbpAuditLogs"); + + migrationBuilder.DropTable( + name: "OpenIddictApplications"); + } + } +} diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/MyProjectNameDbContextModelSnapshot.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/MyProjectNameDbContextModelSnapshot.cs index b4acd389c1..d83e5f0f8f 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/MyProjectNameDbContextModelSnapshot.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/MyProjectNameDbContextModelSnapshot.cs @@ -19,10 +19,10 @@ namespace MyCompanyName.MyProjectName.Host.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "6.0.5") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => { @@ -494,6 +494,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasColumnType("nvarchar(40)") .HasColumnName("ConcurrencyStamp"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -680,6 +683,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasDefaultValue(false) .HasColumnName("EmailConfirmed"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -756,6 +762,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") @@ -958,6 +967,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasColumnType("nvarchar(128)") .HasColumnName("DisplayName"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); @@ -1536,6 +1548,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasColumnType("datetime2") .HasColumnName("DeletionTime"); + b.Property("EntityVersion") + .HasColumnType("int"); + b.Property("ExtraProperties") .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20221220103129_Initial.Designer.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20230216095356_Initial.Designer.cs similarity index 99% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20221220103129_Initial.Designer.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20230216095356_Initial.Designer.cs index e062627c6a..6d0a650f64 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20221220103129_Initial.Designer.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20230216095356_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Mvc.Migrations { [DbContext(typeof(MyProjectNameDbContext))] - [Migration("20221220103129_Initial")] + [Migration("20230216095356_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -765,6 +765,9 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20221220103129_Initial.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20230216095356_Initial.cs similarity index 99% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20221220103129_Initial.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20230216095356_Initial.cs index cebd34749a..0b54955977 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20221220103129_Initial.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20230216095356_Initial.cs @@ -315,6 +315,7 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), AccessFailedCount = table.Column(type: "int", nullable: false, defaultValue: 0), + ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/MyProjectNameDbContextModelSnapshot.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/MyProjectNameDbContextModelSnapshot.cs index 8498fc5efe..5b85316ef3 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/MyProjectNameDbContextModelSnapshot.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/MyProjectNameDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -762,6 +762,9 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBlazorModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBlazorModule.cs index 1033c73a57..fa55b7d44f 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBlazorModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBlazorModule.cs @@ -4,6 +4,7 @@ using Blazorise.Bootstrap5; using Blazorise.Icons.FontAwesome; using Medallion.Threading; using Medallion.Threading.Redis; +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; @@ -163,6 +164,7 @@ public class MyProjectNameBlazorModule : AbpModule .AddCookie("Cookies", options => { options.ExpireTimeSpan = TimeSpan.FromDays(365); + options.IntrospectAccessToken(); }) .AddAbpOpenIdConnect("oidc", options => { diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20221220102812_Initial.Designer.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20230216094625_Initial.Designer.cs similarity index 99% rename from templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20221220102812_Initial.Designer.cs rename to templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20230216094625_Initial.Designer.cs index dc2a86c8b6..dbf5d9beeb 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20221220102812_Initial.Designer.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20230216094625_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { [DbContext(typeof(MyProjectNameDbContext))] - [Migration("20221220102812_Initial")] + [Migration("20230216094625_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -818,6 +818,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20221220102812_Initial.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20230216094625_Initial.cs similarity index 99% rename from templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20221220102812_Initial.cs rename to templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20230216094625_Initial.cs index 42ecfe8c2c..f2a27f9b05 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20221220102812_Initial.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20230216094625_Initial.cs @@ -336,6 +336,7 @@ namespace MyCompanyName.MyProjectName.Migrations LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), AccessFailedCount = table.Column(type: "int", nullable: false, defaultValue: 0), + ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs index 674c8c4fc3..d2f5a653bb 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -815,6 +815,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20221220114625_Initial.Designer.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20230216095438_Initial.Designer.cs similarity index 99% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20221220114625_Initial.Designer.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20230216095438_Initial.Designer.cs index 88bcfda8b0..6c4d2ec371 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20221220114625_Initial.Designer.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20230216095438_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { [DbContext(typeof(AuthServerDbContext))] - [Migration("20221220114625_Initial")] + [Migration("20230216095438_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -765,6 +765,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20221205080257_Initial.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20230216095438_Initial.cs similarity index 99% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20221205080257_Initial.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20230216095438_Initial.cs index cfed2777bd..feed514678 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20221205080257_Initial.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20230216095438_Initial.cs @@ -140,6 +140,7 @@ namespace MyCompanyName.MyProjectName.Migrations ParentId = table.Column(type: "uniqueidentifier", nullable: true), Code = table.Column(type: "nvarchar(95)", maxLength: 95, nullable: false), DisplayName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), CreationTime = table.Column(type: "datetime2", nullable: false), @@ -220,6 +221,7 @@ namespace MyCompanyName.MyProjectName.Migrations IsDefault = table.Column(type: "bit", nullable: false), IsStatic = table.Column(type: "bit", nullable: false), IsPublic = table.Column(type: "bit", nullable: false), + EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true) }, @@ -274,6 +276,7 @@ namespace MyCompanyName.MyProjectName.Migrations { Id = table.Column(type: "uniqueidentifier", nullable: false), Name = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), CreationTime = table.Column(type: "datetime2", nullable: false), @@ -312,6 +315,8 @@ namespace MyCompanyName.MyProjectName.Migrations LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), AccessFailedCount = table.Column(type: "int", nullable: false, defaultValue: 0), + ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), + EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), CreationTime = table.Column(type: "datetime2", nullable: false), diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/AuthServerDbContextModelSnapshot.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/AuthServerDbContextModelSnapshot.cs index 1f8fc82b42..c1120b1b06 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/AuthServerDbContextModelSnapshot.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/AuthServerDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -762,6 +762,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20221220103713_Initial.Designer.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20230216095510_Initial.Designer.cs similarity index 99% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20221220103713_Initial.Designer.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20230216095510_Initial.Designer.cs index 452252dbbf..b2f9ef2b55 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20221220103713_Initial.Designer.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20230216095510_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations { [DbContext(typeof(UnifiedDbContext))] - [Migration("20221220103713_Initial")] + [Migration("20230216095510_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -765,6 +765,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20221220103713_Initial.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20230216095510_Initial.cs similarity index 99% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20221220103713_Initial.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20230216095510_Initial.cs index a0e5f0dcf1..c4d1b42d57 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20221220103713_Initial.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20230216095510_Initial.cs @@ -315,6 +315,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), AccessFailedCount = table.Column(type: "int", nullable: false, defaultValue: 0), + ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs index 54362b1b03..8e2a21c84e 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -762,6 +762,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20221220103825_Initial.Designer.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20230216095454_Initial.Designer.cs similarity index 99% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20221220103825_Initial.Designer.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20230216095454_Initial.Designer.cs index 20c70b78e1..a4aa22d17a 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20221220103825_Initial.Designer.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20230216095454_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { [DbContext(typeof(UnifiedDbContext))] - [Migration("20221220103825_Initial")] + [Migration("20230216095454_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -765,6 +765,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20221220103825_Initial.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20230216095454_Initial.cs similarity index 99% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20221220103825_Initial.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20230216095454_Initial.cs index 88f25f62c6..abf65c1a73 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20221220103825_Initial.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20230216095454_Initial.cs @@ -315,6 +315,7 @@ namespace MyCompanyName.MyProjectName.Migrations LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), LockoutEnabled = table.Column(type: "bit", nullable: false, defaultValue: false), AccessFailedCount = table.Column(type: "int", nullable: false, defaultValue: 0), + ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs index 52bc3189e8..0a6586dd42 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -762,6 +762,9 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnType("nvarchar(256)") .HasColumnName("SecurityStamp"); + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + b.Property("Surname") .HasMaxLength(64) .HasColumnType("nvarchar(64)") diff --git a/tools/localization-key-synchronizer/LocalizationKeySynchronizer.exe b/tools/localization-key-synchronizer/LocalizationKeySynchronizer.exe new file mode 100644 index 0000000000..c8675a0a67 Binary files /dev/null and b/tools/localization-key-synchronizer/LocalizationKeySynchronizer.exe differ diff --git a/tools/localization-key-synchronizer/LocalizationKeySynchronizer.sln b/tools/localization-key-synchronizer/LocalizationKeySynchronizer.sln new file mode 100644 index 0000000000..a9a9ba0988 --- /dev/null +++ b/tools/localization-key-synchronizer/LocalizationKeySynchronizer.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LocalizationKeySynchronizer", "src\LocalizationKeySynchronizer.csproj", "{FFD8DF5E-2724-4D15-9C84-7ACFEEF6F270}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FFD8DF5E-2724-4D15-9C84-7ACFEEF6F270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FFD8DF5E-2724-4D15-9C84-7ACFEEF6F270}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FFD8DF5E-2724-4D15-9C84-7ACFEEF6F270}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FFD8DF5E-2724-4D15-9C84-7ACFEEF6F270}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/tools/localization-key-synchronizer/README.md b/tools/localization-key-synchronizer/README.md new file mode 100644 index 0000000000..f2b9401a30 --- /dev/null +++ b/tools/localization-key-synchronizer/README.md @@ -0,0 +1,27 @@ +# Streamline Localization in Your ABP Project with this Console Tool + +Are you tired of manually managing your localization files for your software projects? Look no further than our new console application designed to streamline your localization workflow! + +![image](https://user-images.githubusercontent.com/58659931/218817197-827d1934-4378-4ccb-87d3-a9118cb5203d.png) + +The application's main menu provides three options: find asynchronous keys, apply changes in the exported file, or replace keys. Let's take a look at each option in more details. + +![image](https://user-images.githubusercontent.com/58659931/218817488-0ba34e67-3039-4162-8291-5eb4669a8868.png) +![image](https://user-images.githubusercontent.com/58659931/218818263-d8d2d8c5-fc77-40a6-ba5c-e1fb7a0180be.png) +![image](https://user-images.githubusercontent.com/58659931/218818359-79a65d48-4895-4ed8-9d34-b6282939ca48.png) + +If you choose to find asynchronous keys, you will be prompted to enter the default language path. Once entered, a multi-select menu will appear with all the JSON files in that folder. You can choose to find keys that do not match the number of arguments, missing keys, or both. If you choose to find the missing keys, you will be prompted to enter the absolute path to export the missing keys. Once entered, the export process will begin, and the application will close. + +![image](https://user-images.githubusercontent.com/58659931/218818963-747766a5-b1c0-420f-95d2-7017a5f63925.png) + +If you choose to find the keys that do not match the number of arguments, you can choose to delete, export, or both. If you choose to delete, the process will complete, and the application will close. If you choose to export, you will be prompted to enter the export path. Once entered, the export process will begin, and the application will close. If you choose to do both, both processes will complete, and the application will close. + +![image](https://user-images.githubusercontent.com/58659931/218820012-e1596f88-5916-4f4f-a482-402c692ed207.png) + +If you choose to apply changes in the exported file, you will be prompted to import the file, and the process will complete. + +![image](https://user-images.githubusercontent.com/58659931/218820350-bfc0ccae-06e8-46d6-853d-7a2d1df3b156.png) + + If you choose to replace keys, you will be prompted to enter the localization folder path, the old key, the new key, and select the JSON files where you want the changes to be made. Once all information is entered, the application will begin the replacement process, and it will close. + +Overall, our console application offers a simple and effective solution to manage your localization files. Try it out yourself today! diff --git a/tools/localization-key-synchronizer/src/AbpAsyncKey.cs b/tools/localization-key-synchronizer/src/AbpAsyncKey.cs new file mode 100644 index 0000000000..e46bb2d7be --- /dev/null +++ b/tools/localization-key-synchronizer/src/AbpAsyncKey.cs @@ -0,0 +1,16 @@ +namespace LocalizationKeySynchronizer; + +public class AbpAsyncKey +{ + public string NewValue = string.Empty; + + public AbpAsyncKey(string key, string reference) + { + Key = key; + Reference = reference; + } + + public virtual string Type => GetType().Name; + public string Key { get; set; } + public string Reference { get; set; } +} \ No newline at end of file diff --git a/tools/localization-key-synchronizer/src/AbpAsyncLocalization.cs b/tools/localization-key-synchronizer/src/AbpAsyncLocalization.cs new file mode 100644 index 0000000000..396975f5b7 --- /dev/null +++ b/tools/localization-key-synchronizer/src/AbpAsyncLocalization.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace LocalizationKeySynchronizer; + +public class AbpAsyncLocalization +{ + public AbpAsyncLocalization(AbpLocalization localization, AbpLocalization reference, List asyncKeys) + { + Localization = localization; + Reference = reference; + AsyncKeys = asyncKeys; + } + + public AbpLocalization Localization { get; set; } + public AbpLocalization Reference { get; set; } + + public List AsyncKeys { get; set; } +} \ No newline at end of file diff --git a/tools/localization-key-synchronizer/src/AbpAsyncLocalizationViewModel.cs b/tools/localization-key-synchronizer/src/AbpAsyncLocalizationViewModel.cs new file mode 100644 index 0000000000..c2a17ae378 --- /dev/null +++ b/tools/localization-key-synchronizer/src/AbpAsyncLocalizationViewModel.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace LocalizationKeySynchronizer; + +public class AbpAsyncLocalizationViewModel +{ + public AbpAsyncLocalizationViewModel(string referenceCulture, string culture, string path, List asyncKeys) + { + ReferenceCulture = referenceCulture; + Culture = culture; + Path = path; + AsyncKeys = asyncKeys; + } + + public string ReferenceCulture { get; set; } + public string Culture { get; set; } + public string Path { get; set; } + + public List AsyncKeys { get; set; } +} \ No newline at end of file diff --git a/tools/localization-key-synchronizer/src/AbpLocalization.cs b/tools/localization-key-synchronizer/src/AbpLocalization.cs new file mode 100644 index 0000000000..78d82d556a --- /dev/null +++ b/tools/localization-key-synchronizer/src/AbpLocalization.cs @@ -0,0 +1,14 @@ +namespace LocalizationKeySynchronizer; + +public class AbpLocalization +{ + public AbpLocalization(string filePath, AbpLocalizationInfo localizationInfo) + { + FilePath = filePath; + LocalizationInfo = localizationInfo; + } + + public string FilePath { get; set; } + + public AbpLocalizationInfo LocalizationInfo { get; set; } +} \ No newline at end of file diff --git a/tools/localization-key-synchronizer/src/AbpLocalizationInfo.cs b/tools/localization-key-synchronizer/src/AbpLocalizationInfo.cs new file mode 100644 index 0000000000..d8db3b4b1b --- /dev/null +++ b/tools/localization-key-synchronizer/src/AbpLocalizationInfo.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace LocalizationKeySynchronizer; + +// This class is used to deserialize the JSON string from culture file. +public class AbpLocalizationInfo +{ + public AbpLocalizationInfo(string culture, Dictionary texts) + { + Culture = culture; + Texts = texts; + } + + public string Culture { get; set; } + public Dictionary Texts { get; set; } + + public static bool TryDeserialize(string json, out AbpLocalizationInfo? localizationInfo) + { + return JsonHelper.TryDeserialize(json, out localizationInfo); + } +} \ No newline at end of file diff --git a/tools/localization-key-synchronizer/src/ArgumentCountMismatch.cs b/tools/localization-key-synchronizer/src/ArgumentCountMismatch.cs new file mode 100644 index 0000000000..1b3630f116 --- /dev/null +++ b/tools/localization-key-synchronizer/src/ArgumentCountMismatch.cs @@ -0,0 +1,15 @@ +namespace LocalizationKeySynchronizer; + +public class ArgumentCountMismatch : AbpAsyncKey +{ + public ArgumentCountMismatch(string key, string reference, int referenceArgumentCount, int argumentCount, string value) : base(key, reference) + { + ReferenceArgumentCount = referenceArgumentCount; + ArgumentCount = argumentCount; + Value = value; + } + + public int ReferenceArgumentCount { get; } + public int ArgumentCount { get; } + public string Value { get; } +} \ No newline at end of file diff --git a/tools/localization-key-synchronizer/src/JsonHelper.cs b/tools/localization-key-synchronizer/src/JsonHelper.cs new file mode 100644 index 0000000000..2f284d5839 --- /dev/null +++ b/tools/localization-key-synchronizer/src/JsonHelper.cs @@ -0,0 +1,26 @@ +using System; +using Newtonsoft.Json; + +namespace LocalizationKeySynchronizer; + +public static class JsonHelper +{ + public static bool TryDeserialize(string json, out T? result) + { + try + { + result = JsonConvert.DeserializeObject(json); + return true; + } + catch (Exception) + { + result = default; + return false; + } + } + + public static string Serialize(T value) + { + return JsonConvert.SerializeObject(value, Formatting.Indented); + } +} \ No newline at end of file diff --git a/tools/localization-key-synchronizer/src/LocalizationHelper.cs b/tools/localization-key-synchronizer/src/LocalizationHelper.cs new file mode 100644 index 0000000000..fbdb8a1aef --- /dev/null +++ b/tools/localization-key-synchronizer/src/LocalizationHelper.cs @@ -0,0 +1,173 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +namespace LocalizationKeySynchronizer; + +public static partial class LocalizationHelper +{ + public static bool TryGetLocalization(string path, out AbpLocalizationInfo? localizationInfo) + { + if (File.Exists(path) == false) + { + localizationInfo = default; + return false; + } + + var json = File.ReadAllTextAsync(path).GetAwaiter().GetResult(); + return AbpLocalizationInfo.TryDeserialize(json, out localizationInfo); + } + + public static List GetLocalizations(IEnumerable paths) + { + var results = new List(); + foreach (var path in paths) + { + if (TryGetLocalization(path, out var localizationInfo)) + { + results.Add(new AbpLocalization(path, localizationInfo!)); + } + } + + return results; + } + + private static Dictionary GetKeysAndArgCount(this AbpLocalizationInfo localizationInfo) + { + return localizationInfo.Texts.ToDictionary(k => k.Key, v => GetArgCount(v.Value)); + } + + private static int GetArgCount(string value) + { + var matches = MyRegex().Matches(value); + return matches.Count; + } + + public static List GetAsynchronousLocalizations(this AbpLocalization defaultLocalization, + IEnumerable otherLocalizations) + { + var results = new List(); + var defaultCultureKeysAndArgCount = defaultLocalization.LocalizationInfo.GetKeysAndArgCount(); + foreach (var localization in otherLocalizations) + { + var keysAndArgCount = localization.LocalizationInfo.GetKeysAndArgCount(); + var asynchronousResource = + new AbpAsyncLocalization(localization, defaultLocalization, new List()); + foreach (var (key, defaultCultureArgCount) in defaultCultureKeysAndArgCount) + { + if (keysAndArgCount.TryGetValue(key, out var value)) + { + if (value != defaultCultureArgCount) + { + asynchronousResource.AsyncKeys.Add(new ArgumentCountMismatch(key, + defaultLocalization.LocalizationInfo.Texts[key], defaultCultureArgCount, value, + localization.LocalizationInfo.Texts[key])); + } + } + else + { + asynchronousResource.AsyncKeys.Add(new MissingKey(key, + defaultLocalization.LocalizationInfo.Texts[key])); + } + } + + if (asynchronousResource.AsyncKeys.Any()) + { + results.Add(asynchronousResource); + } + } + + return results; + } + + public static void DeleteKeysThatDoNotMatchTheNumberOfArguments( + IEnumerable asynchronousResources) + { + foreach (var resource in asynchronousResources) + { + foreach (var key in resource.AsyncKeys.Select(x => x.Key)) + { + resource.Localization.LocalizationInfo.Texts.Remove(key); + } + + File.WriteAllTextAsync(resource.Localization.FilePath, + JsonHelper.Serialize(resource.Localization.LocalizationInfo)).GetAwaiter().GetResult(); + } + } + + public static void ExportKeysThatDoNotMatchTheNumberOfArguments( + IEnumerable asynchronousResources, string? exportPath) + { + Export(asynchronousResources, exportPath); + } + + public static void Export(IEnumerable asynchronousResources, string? exportPath) + where T : AbpAsyncKey + { + var asyncLocalizationViewModels = asynchronousResources.Select(x => + new AbpAsyncLocalizationViewModel(x.Reference.LocalizationInfo.Culture, + x.Localization.LocalizationInfo.Culture, x.Localization.FilePath, + x.AsyncKeys.Where(k => k is T).ToList())).ToList(); + + if (exportPath != null) + { + File.WriteAllTextAsync(exportPath, + JsonHelper.Serialize(asyncLocalizationViewModels)) + .GetAwaiter().GetResult(); + } + } + + public static void ExportMissingKeys(IEnumerable asyncLocalizations, string? exportPath) + { + Export(asyncLocalizations, exportPath); + } + + public static bool ApplyChanges(string path) + { + var json = File.ReadAllTextAsync(path).GetAwaiter().GetResult(); + + if (JsonHelper.TryDeserialize(json, out List? asyncLocalizationViewModels) == + false) + { + return false; + } + + foreach (var asyncLocalizationViewModel in asyncLocalizationViewModels!) + { + if (TryGetLocalization(asyncLocalizationViewModel.Path, out var localizationInfo) == false) + { + return false; + } + + foreach (var asyncKey in asyncLocalizationViewModel.AsyncKeys.Where(asyncKey => + !string.IsNullOrWhiteSpace(asyncKey.NewValue))) + { + localizationInfo!.Texts[asyncKey.Key] = asyncKey.NewValue; + } + + File.WriteAllTextAsync(asyncLocalizationViewModel.Path, + JsonHelper.Serialize(localizationInfo)).GetAwaiter().GetResult(); + } + + return true; + } + + public static void ReplaceKey(string oldKey, string newKey, List localizations) + { + foreach (var localization in localizations) + { + if (!localization.LocalizationInfo.Texts.TryGetValue(oldKey, out var value)) + { + continue; + } + + localization.LocalizationInfo.Texts.Remove(oldKey); + localization.LocalizationInfo.Texts.Add(newKey, value); + File.WriteAllTextAsync(localization.FilePath, + JsonHelper.Serialize(localization.LocalizationInfo)).GetAwaiter().GetResult(); + } + } + + [GeneratedRegex("{(\\d+)}")] + private static partial Regex MyRegex(); +} \ No newline at end of file diff --git a/tools/localization-key-synchronizer/src/LocalizationKeySynchronizer.csproj b/tools/localization-key-synchronizer/src/LocalizationKeySynchronizer.csproj new file mode 100644 index 0000000000..8f566bc239 --- /dev/null +++ b/tools/localization-key-synchronizer/src/LocalizationKeySynchronizer.csproj @@ -0,0 +1,15 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + diff --git a/tools/localization-key-synchronizer/src/MissingKey.cs b/tools/localization-key-synchronizer/src/MissingKey.cs new file mode 100644 index 0000000000..0463f3dd16 --- /dev/null +++ b/tools/localization-key-synchronizer/src/MissingKey.cs @@ -0,0 +1,8 @@ +namespace LocalizationKeySynchronizer; + +public class MissingKey : AbpAsyncKey +{ + public MissingKey(string key, string reference) : base(key, reference) + { + } +} \ No newline at end of file diff --git a/tools/localization-key-synchronizer/src/Program.cs b/tools/localization-key-synchronizer/src/Program.cs new file mode 100644 index 0000000000..42c97f0754 --- /dev/null +++ b/tools/localization-key-synchronizer/src/Program.cs @@ -0,0 +1,173 @@ +using System; +using System.IO; +using System.Linq; +using LocalizationKeySynchronizer; +using Spectre.Console; +using static LocalizationKeySynchronizer.Questions; + +try +{ + // Do you want to find asynchronous keys, apply changes in the exported file or replace the keys? + var option = AnsiConsole.Prompt( + new SelectionPrompt() + .Title(_1.Question) + .AddChoices(_1.Options.Find, _1.Options.Apply, _1.Options.Replace)); + + switch (option) + { + case _1.Options.Apply: + { + // Enter the absolute path to the exported file: + var path = AnsiConsole.Ask(_2); + + if (!File.Exists(path)) + { + AnsiConsole.MarkupLine("[red]The file does not exist![/]"); + Exit(); + } + + if (LocalizationHelper.ApplyChanges(path)) + { + AnsiConsole.MarkupLine("[green]The changes have been applied successfully![/]"); + Exit(); + } + + AnsiConsole.MarkupLine("[red]An error occurred while applying changes![/]"); + Exit(); + break; + } + case _1.Options.Find: + { + // The default language path + var path = AnsiConsole.Ask(_3); + + if (!LocalizationHelper.TryGetLocalization(path, out var defaultLocalizationInfo)) + { + AnsiConsole.MarkupLine("[red]The default language path is invalid![/]"); + Exit(); + } + + var defaultCulture = new AbpLocalization(path, defaultLocalizationInfo!); + +// Get others cultures + var paths = Directory.GetFiles(Path.GetDirectoryName(path)!, "*.json", + SearchOption.TopDirectoryOnly); + + var otherCulturePaths = paths.Select(Path.GetFileNameWithoutExtension).Where(x=>!string.IsNullOrWhiteSpace(x)).Select(x=>x!).ToList(); + // select other cultures + paths = AnsiConsole.Prompt( + new MultiSelectionPrompt() + .Title("Select other cultures") + .PageSize(10).AddChoiceGroup("All", otherCulturePaths)) + .Select(x => Path.Combine(Path.GetDirectoryName(path)!, x + ".json")) + .ToArray(); + + var otherCultures = LocalizationHelper.GetLocalizations(paths); + var asyncLocalizations = defaultCulture.GetAsynchronousLocalizations(otherCultures); + +// Find keys that do not match the number of arguments, find missing keys, or both + + var options = AnsiConsole.Prompt( + new MultiSelectionPrompt() + .Title(_4.Question) + .PageSize(10) + .AddChoiceGroup("All", _4.Options.ArgumentsCount, _4.Options.MissingKeys)); + +// For arguments +// Find keys that do not match the number of arguments + + string? exportPath = null; + if (options.Contains(_4.Options.ArgumentsCount)) + { + // Should the keys that do not match the number of arguments be deleted, exported or both? + + var options2 = AnsiConsole.Prompt( + new MultiSelectionPrompt() + .Title(_5.Question) + .PageSize(10) + .AddChoiceGroup("All", _5.Options.Delete, _5.Options.Export)); + + // Delete the keys that do not match the number of arguments + if (options2.Contains(_5.Options.Delete)) + { + LocalizationHelper.DeleteKeysThatDoNotMatchTheNumberOfArguments(asyncLocalizations); + } + + // Ask for the export path and export it + if (options2.Contains(_5.Options.Export)) + { + if (options.Contains(_4.Options.MissingKeys)) + { + exportPath = AnsiConsole.Ask(_8); + LocalizationHelper.Export(asyncLocalizations, exportPath); + } + else + { + exportPath = AnsiConsole.Ask(_6); + LocalizationHelper.ExportKeysThatDoNotMatchTheNumberOfArguments(asyncLocalizations, exportPath); + } + } + } + +// For missing keys +// Export missing keys + if (options.Contains(_4.Options.MissingKeys)) + { + if (string.IsNullOrEmpty(exportPath)) + { + exportPath = AnsiConsole.Ask(_7); + LocalizationHelper.ExportMissingKeys(asyncLocalizations, exportPath); + } + } + + break; + } + case _1.Options.Replace: + { + // The localization folder path + var path = AnsiConsole.Ask(_9); + + // Old key + var oldKey = AnsiConsole.Ask(_10); + + // New key + var newKey = AnsiConsole.Ask(_11); + + // Localization paths + var paths = Directory.GetFiles(path, "*.json", + SearchOption.TopDirectoryOnly); + + // Select localizations + + var localizationPaths = paths.Select(Path.GetFileNameWithoutExtension).Where(x=>!string.IsNullOrWhiteSpace(x)).Select(x=>x!).ToList(); + paths = AnsiConsole.Prompt( + new MultiSelectionPrompt() + .Title("Select localizations") + .PageSize(10) + .AddChoiceGroup("All", localizationPaths)) + .Select(x => Path.Combine(path, x + ".json")) + .ToArray(); + + var cultures = LocalizationHelper.GetLocalizations(paths); + + // Replace keys + LocalizationHelper.ReplaceKey(oldKey, newKey, cultures); + + AnsiConsole.MarkupLine("[green]The keys have been replaced successfully![/]"); + break; + } + } +} +catch (Exception e) +{ + Console.WriteLine(e); + AnsiConsole.MarkupLine($"[red]{e.Message}[/]"); + Exit(); +} + +void Exit() +{ + AnsiConsole.MarkupLine("[red]Press any key to exit...[/]"); + Console.ReadKey(); + Environment.Exit(0); +} \ No newline at end of file diff --git a/tools/localization-key-synchronizer/src/Questions.cs b/tools/localization-key-synchronizer/src/Questions.cs new file mode 100644 index 0000000000..a284fbaf9b --- /dev/null +++ b/tools/localization-key-synchronizer/src/Questions.cs @@ -0,0 +1,57 @@ +namespace LocalizationKeySynchronizer; + +public static class Questions +{ + public const string _2 = "Enter the absolute path to the exported file:"; + + public const string _3 = "Enter the default language path:"; + + public const string _6 = "Enter the absolute path to export the keys that do not match the number of arguments:"; + + public const string _7 = "Enter the absolute path to export the missing keys:"; + + public const string _8 = "Enter the export path:"; + + public const string _9 = "Enter the localization folder path:"; + + public const string _10 = "Enter the old key:"; + + public const string _11 = "Enter the new key:"; + + public static class _1 + { + public const string Question = + "Do you want to find asynchronous keys, apply changes in the exported file or replace the keys?"; + + public static class Options + { + public const string Find = "Find asynchronous keys"; + public const string Apply = "Apply changes in the exported file"; + public const string Replace = "Replace keys"; + } + } + + public static class _4 + { + public const string Question = + "Find keys that do not match the number of arguments, find missing keys, or both?"; + + public static class Options + { + public const string ArgumentsCount = "Not matching arguments count"; + public const string MissingKeys = "Missing keys"; + } + } + + public static class _5 + { + public const string Question = + "Should the keys that do not match the number of arguments be deleted, exported or both?"; + + public static class Options + { + public const string Delete = "Delete"; + public const string Export = "Export"; + } + } +} \ No newline at end of file