From abf75f88953e6ac006a3a6fbff28bbedd1d23e5c Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Tue, 8 Mar 2022 13:55:05 +0300 Subject: [PATCH 001/185] Blogging: Convert tags to lowercase resolves https://github.com/abpframework/abp/issues/11699 --- .../Volo/Blogging/Posts/PostAppService.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs index 457656291f..15506ecf70 100644 --- a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs +++ b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs @@ -231,11 +231,16 @@ namespace Volo.Blogging.Posts return url; } - private async Task SaveTags(ICollection newTags, Post post) + private async Task SaveTags(ICollection tags, Post post) { - await RemoveOldTags(newTags, post); - - await AddNewTags(newTags, post); + tags = tags + .Select(t => t.ToLowerInvariant()) + .Distinct() + .ToList(); + + await RemoveOldTags(tags, post); + + await AddNewTags(tags, post); } private async Task RemoveOldTags(ICollection newTags, Post post) From f36cb72e1a41d38d7b24259f9a76949a0feecd29 Mon Sep 17 00:00:00 2001 From: muhammedaltug Date: Wed, 9 Mar 2022 15:27:55 +0300 Subject: [PATCH 002/185] update en docs --- docs/en/UI/Angular/Config-State-Service.md | 2 +- docs/en/UI/Angular/Confirmation-Service.md | 31 +++++------ docs/en/UI/Angular/Environment.md | 62 ++++++++++------------ docs/en/UI/Angular/Quick-Start.md | 2 +- docs/en/UI/Angular/Testing.md | 4 +- docs/en/UI/Angular/Toaster-Service.md | 18 +++---- 6 files changed, 55 insertions(+), 64 deletions(-) diff --git a/docs/en/UI/Angular/Config-State-Service.md b/docs/en/UI/Angular/Config-State-Service.md index 3c1930d908..b3884f65e1 100644 --- a/docs/en/UI/Angular/Config-State-Service.md +++ b/docs/en/UI/Angular/Config-State-Service.md @@ -110,7 +110,7 @@ this.config.getSetting$("Abp.Identity.TwoFactor.Behaviour").subscribe(twoFactorB #### State Properties -Please refer to `ApplicationConfiguration.Response` type for all the properties you can get with `getOne` and `getDeep`. It can be found in the [application-configuration.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/application-configuration.ts#L4). +Please refer to `ApplicationConfigurationDto` type for all the properties you can get with `getOne` and `getDeep`. It can be found in the [models.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/application-configurations/models.ts#L11). ## Set State diff --git a/docs/en/UI/Angular/Confirmation-Service.md b/docs/en/UI/Angular/Confirmation-Service.md index ef48332580..3c864d2bba 100644 --- a/docs/en/UI/Angular/Confirmation-Service.md +++ b/docs/en/UI/Angular/Confirmation-Service.md @@ -2,12 +2,10 @@ You can use the `ConfirmationService` in @abp/ng.theme.shared package to display a confirmation popup by placing at the root level in your project. - ## Getting Started You do not have to provide the `ConfirmationService` at module or component level, because it is already **provided in root**. You can inject and start using it immediately in your components, directives, or services. - ```js import { ConfirmationService } from '@abp/ng.theme.shared'; @@ -26,7 +24,7 @@ You can use the `success`, `warn`, `error`, and `info` methods of `ConfirmationS ### How to Display a Confirmation Popup ```js -const confirmationStatus$ = this.confirmation.success('Message', 'Title'); +const confirmationStatus$ = this.confirmation.success("Message", "Title"); ``` - The `ConfirmationService` methods accept three parameters that are `message`, `title`, and `options`. @@ -48,18 +46,16 @@ this.confirmation }); ``` - - The `message` and `title` parameters accept a string, localization key or localization object. See the [localization document](./Localization.md) - `Confirmation.Status` is an enum and has three properties; - - `Confirmation.Status.confirm` is a closing event value that will be emitted when the popup is closed by the confirm button. - - `Confirmation.Status.reject` is a closing event value that will be emitted when the popup is closed by the cancel button. - - `Confirmation.Status.dismiss` is a closing event value that will be emitted when the popup is closed by pressing the escape or clicking the backdrop. - + - `Confirmation.Status.confirm` is a closing event value that will be emitted when the popup is closed by the confirm button. + - `Confirmation.Status.reject` is a closing event value that will be emitted when the popup is closed by the cancel button. + - `Confirmation.Status.dismiss` is a closing event value that will be emitted when the popup is closed by pressing the escape or clicking the backdrop. If you are not interested in the confirmation status, you do not have to subscribe to the returned observable: ```js -this.confirmation.error('You are not authorized.', 'Error'); +this.confirmation.error("You are not authorized.", "Error"); ``` ### How to Display a Confirmation Popup With Given Options @@ -71,16 +67,16 @@ const options: Partial = { hideCancelBtn: false, hideYesBtn: false, dismissible: false, - cancelText: 'Close', - yesText: 'Confirm', - messageLocalizationParams: ['Demo'], + cancelText: "Close", + yesText: "Confirm", + messageLocalizationParams: ["Demo"], titleLocalizationParams: [], }; this.confirmation.warn( - 'AbpIdentity::RoleDeletionConfirmationMessage', - 'Are you sure?', - options, + "AbpIdentity::RoleDeletionConfirmationMessage", + "Are you sure?", + options ); ``` @@ -110,7 +106,7 @@ this.confirmation.warn( Do you confirm that? `, 'Are you sure?', - options, + options ); ``` @@ -138,8 +134,7 @@ success( ): Observable ``` -> See the [`Config.LocalizationParam` type](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/core/src/lib/models/config.ts#L46) and [`Confirmation` namespace](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/theme-shared/src/lib/models/confirmation.ts) - +> See the [`LocalizationParam` type](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/core/src/lib/models/localization.ts#L6) and [`Confirmation` namespace](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/theme-shared/src/lib/models/confirmation.ts) ### warn diff --git a/docs/en/UI/Angular/Environment.md b/docs/en/UI/Angular/Environment.md index 9802eb6264..026c5c5fc7 100644 --- a/docs/en/UI/Angular/Environment.md +++ b/docs/en/UI/Angular/Environment.md @@ -1,6 +1,6 @@ # Environment -Every application needs some **environment** variables. In Angular world, this is usually managed by `environment.ts`, `environment.prod.ts` and so on. It is the same for ABP as well. +Every application needs some **environment** variables. In Angular world, this is usually managed by `environment.ts`, `environment.prod.ts` and so on. It is the same for ABP as well. Current `Environment` configuration holds sub config classes as follows: @@ -30,7 +30,7 @@ export interface ApiConfig { ``` Api config has to have a default config and it may have some additional ones for different modules. -I.e. you may want to connect to different Apis for different modules. +I.e. you may want to connect to different Apis for different modules. Take a look at following example @@ -39,35 +39,34 @@ Take a look at following example // ... "apis": { "default": { - "url": "https://localhost:8080", + "url": "https://localhost:8080" }, "AbpIdentity": { - "url": "https://localhost:9090", + "url": "https://localhost:9090" } - }, + } // ... } ``` -When an api from `AbpIdentity` is called, the request will be sent to `"https://localhost:9090"`. +When an api from `AbpIdentity` is called, the request will be sent to `"https://localhost:9090"`. Everything else will be sent to `"https://localhost:8080"` -* `rootNamespace` **(new)** : Root namespace of the related API. e.g. Acme.BookStore +- `rootNamespace` **(new)** : Root namespace of the related API. e.g. Acme.BookStore ## Application ```js - export interface Application { +export interface Application { name: string; baseUrl?: string; logoUrl?: string; } ``` -* `name`: Name of the backend Application. It is also used by `logo.component` if `logoUrl` is not provided. -* `logoUrl`: Url of the application logo. It is used by `logo.component` -* `baseUrl`: [For detailed information](./Multi-Tenancy.md#domain-tenant-resolver) - +- `name`: Name of the backend Application. It is also used by `logo.component` if `logoUrl` is not provided. +- `logoUrl`: Url of the application logo. It is used by `logo.component` +- `baseUrl`: [For detailed information](./Multi-Tenancy.md#domain-tenant-resolver) ## AuthConfig @@ -75,7 +74,7 @@ For authentication, we use angular-oauth2-oidc. Please check their [docs](https: ## RemoteEnvironment -Some applications need to integrate an existing config into the `environment` used throughout the application. +Some applications need to integrate an existing config into the `environment` used throughout the application. Abp Framework supports this out of box. To integrate an existing config json into the `environment`, you need to set `remoteEnv` @@ -83,29 +82,28 @@ To integrate an existing config json into the `environment`, you need to set `re ```js export type customMergeFn = ( localEnv: Partial, - remoteEnv: any, + remoteEnv: any ) => Config.Environment; export interface RemoteEnv { url: string; - mergeStrategy: 'deepmerge' | 'overwrite' | customMergeFn; + mergeStrategy: "deepmerge" | "overwrite" | customMergeFn; method?: string; headers?: ABP.Dictionary; } ``` -* `url` *: Required. The url to be used to retrieve environment config -* `mergeStrategy` *: Required. Defines how the local and the remote `environment` json will be merged - * `deepmerge`: Both local and remote `environment` json will be merged recursively. If both configs have same nested path, the remote `environment` will be prioritized. - * `overwrite`: Remote `environment` will be used and local environment will be ignored. - * `customMergeFn`: You can also provide your own merge function as shown in the example. It will take two parameters, `localEnv: Partial` and `remoteEnv` and it needs to return a `Config.Environment` object. -* `method`: HTTP method to be used when retrieving environment config. Default: `GET` -* `headers`: If extra headers are needed for the request, it can be set through this field. +- `url` \*: Required. The url to be used to retrieve environment config +- `mergeStrategy` \*: Required. Defines how the local and the remote `environment` json will be merged + - `deepmerge`: Both local and remote `environment` json will be merged recursively. If both configs have same nested path, the remote `environment` will be prioritized. + - `overwrite`: Remote `environment` will be used and local environment will be ignored. + - `customMergeFn`: You can also provide your own merge function as shown in the example. It will take two parameters, `localEnv: Partial` and `remoteEnv` and it needs to return a `Config.Environment` object. +- `method`: HTTP method to be used when retrieving environment config. Default: `GET` +- `headers`: If extra headers are needed for the request, it can be set through this field. ## EnvironmentService -` EnvironmentService` is a singleton service, i.e. provided in root level of your application, and keeps the environment in the internal store. - +` EnvironmentService` is a singleton service, i.e. provided in root level of your application, and keeps the environment in the internal store. ### Before Use @@ -124,7 +122,6 @@ class DemoComponent { You do not have to provide the `EnvironmentService` at module or component/directive level, because it is already **provided in root**. - ### Get Methods `EnvironmentService` has numerous get methods which allow you to get a specific value or all environment object. @@ -141,9 +138,9 @@ You can use the `getEnvironment` or `getEnvironment$` method of `EnvironmentServ const environment = this.environment.getEnvironment(); // or -this.environment.getEnvironment$().subscribe(environment => { - // use environment here -}) +this.environment.getEnvironment$().subscribe((environment) => { + // use environment here +}); ``` #### How to Get API URL @@ -156,14 +153,13 @@ The `getApiUrl` or `getApiUrl$` method is used to get a specific API URL from th const apiUrl = this.environment.getApiUrl(); // environment.apis.default.url -this.environment.getApiUrl$("search").subscribe(searchUrl => { -// environment.apis.search.url -}) +this.environment.getApiUrl$("search").subscribe((searchUrl) => { + // environment.apis.search.url +}); ``` This method returns the `url` of a specific API based on the key given as its only parameter. If there is no key, `'default'` is used. - #### How to Set the Environment `EnvironmentService` has a method named `setState` which allows you to set the state value. @@ -178,4 +174,4 @@ Note that **you do not have to call this method at application initiation**, bec #### Environment Properties -Please refer to `Environment` type for all the properties. It can be found in the [config.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/config.ts#L13). \ No newline at end of file +Please refer to `Environment` type for all the properties. It can be found in the [environment.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/environment.ts#L4). diff --git a/docs/en/UI/Angular/Quick-Start.md b/docs/en/UI/Angular/Quick-Start.md index c17f069c5f..b487a1472b 100644 --- a/docs/en/UI/Angular/Quick-Start.md +++ b/docs/en/UI/Angular/Quick-Start.md @@ -198,7 +198,7 @@ In addition, you can [deploy your application to certain targets using the Angul - [Azure](https://github.com/Azure/ng-deploy-azure#readme) - [Firebase](https://github.com/angular/angularfire#readme) - [Netlify](https://github.com/ngx-builders/netlify-builder#readme) -- [Vercel](https://github.com/vercel/ng-deploy-vercel#readme) +- [Vercel `vercel init angular`](https://github.com/vercel/vercel/tree/main/examples/angular) - [GitHub Pages](https://github.com/angular-schule/angular-cli-ghpages/#readme) --- diff --git a/docs/en/UI/Angular/Testing.md b/docs/en/UI/Angular/Testing.md index bf6d3e1b26..bd40c7b55f 100644 --- a/docs/en/UI/Angular/Testing.md +++ b/docs/en/UI/Angular/Testing.md @@ -123,7 +123,7 @@ The **queries in Angular Testing Library follow practices for maintainable tests - [Queries](https://testing-library.com/docs/dom-testing-library/api-queries) - [User Event](https://testing-library.com/docs/ecosystem-user-event) -- [Examples](https://github.com/testing-library/angular-testing-library/tree/master/apps/example-app/app/examples) +- [Examples](https://github.com/testing-library/angular-testing-library/tree/main/apps/example-app/src/app/examples) ### Clearing DOM After Each Spec @@ -377,4 +377,4 @@ npm test -- --prod ## See Also -* [ABP Community Video - Unit Testing with the Angular UI](https://community.abp.io/articles/unit-testing-with-the-angular-ui-p4l550q3) +- [ABP Community Video - Unit Testing with the Angular UI](https://community.abp.io/articles/unit-testing-with-the-angular-ui-p4l550q3) diff --git a/docs/en/UI/Angular/Toaster-Service.md b/docs/en/UI/Angular/Toaster-Service.md index 059fd720a8..3152eba1af 100644 --- a/docs/en/UI/Angular/Toaster-Service.md +++ b/docs/en/UI/Angular/Toaster-Service.md @@ -2,12 +2,10 @@ You can use the `ToasterService` in @abp/ng.theme.shared package to display messages in an overlay by placing at the root level in your project. - ## Getting Started You do not have to provide the `ToasterService` at module or component level, because it is already **provided in root**. You can inject and start using it immediately in your components, directives, or services. - ```js import { ToasterService } from '@abp/ng.theme.shared'; @@ -26,7 +24,7 @@ You can use the `success`, `warn`, `error`, and `info` methods of `ToasterServic ### How to Display a Toast Overlay ```js -this.toaster.success('Message', 'Title'); +this.toaster.success("Message", "Title"); ``` - The `ToasterService` methods accept three parameters that are `message`, `title`, and `options`. @@ -72,12 +70,11 @@ With the options above, the toast overlay looks like this: The open toast overlay can be removed manually via the `remove` method by passing the `id` of toast: ```js -const toastId = this.toaster.success('Message', 'Title') +const toastId = this.toaster.success("Message", "Title"); this.toaster.remove(toastId); ``` - ### How to Remove All Toasts The all open toasts can be removed manually via the `clear` method: @@ -85,11 +82,13 @@ The all open toasts can be removed manually via the `clear` method: ```js this.toaster.clear(); ``` + ## Replacing ToasterService with 3rd party toaster libraries If you want the ABP Framework to utilize 3rd party libraries for the toasters instead of the built-in one, you can provide a service that implements `Toaster.Service` interface, and provide it as follows (ngx-toastr library used in example): -> You can use *LocalizationService* for toaster messages translations. +> You can use _LocalizationService_ for toaster messages translations. + ```js // your-custom-toaster.service.ts import { Injectable } from '@angular/core'; @@ -167,6 +166,7 @@ export class CustomToasterService implements Toaster.Service { } } ``` + ```js // app.module.ts @@ -178,10 +178,11 @@ import { ToasterService } from '@abp/ng.theme.shared'; { provide: ToasterService, useClass: CustomToasterService, - }, + }, ] }) ``` + ## API ### success @@ -197,8 +198,7 @@ success( - `Config` namespace can be imported from `@abp/ng.core`. - `Toaster` namespace can be imported from `@abp/ng.theme.shared`. -> See the [`Config.LocalizationParam` type](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/core/src/lib/models/config.ts#L46) and [`Toaster` namespace](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/theme-shared/src/lib/models/toaster.ts) - +> See the [`LocalizationParam` type](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/core/src/lib/models/localization.ts#L6) and [`Toaster` namespace](https://github.com/abpframework/abp/blob/master/npm/ng-packs/packages/theme-shared/src/lib/models/toaster.ts) ### warn From 731c4b1154706c8316fce46bbed70fd01548eb09 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 10 Mar 2022 18:59:45 +0800 Subject: [PATCH 003/185] Add `HandleContextAsync` method to the `IFeatureManagementProvider`. --- .../Volo/Abp/AsyncDisposeFunc.cs | 30 +++++++++++++ .../Volo/Abp/NullAsyncDisposable.cs | 19 +++++++++ .../AbpFeatureManagementDomainModule.cs | 4 +- .../DefaultValueFeatureManagementProvider.cs | 8 +++- .../FeatureManagementProvider.cs | 8 +++- .../Abp/FeatureManagement/FeatureManager.cs | 13 +++--- .../IFeatureManagementProvider.cs | 6 ++- .../TenantFeatureManagementProvider.cs | 21 +++++++++- .../FeatureManagement/FeatureManager_Tests.cs | 42 +++++++++++++++++++ .../FeatureManagementTestBaseModule.cs | 9 ++-- .../FeatureManagementTestDataBuilder.cs | 22 ++++++++++ .../NextTenantFeatureManagementProvider.cs | 32 ++++++++++++++ .../Abp/FeatureManagement/TestEditionIds.cs | 3 ++ 13 files changed, 203 insertions(+), 14 deletions(-) create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/AsyncDisposeFunc.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/NullAsyncDisposable.cs create mode 100644 modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/NextTenantFeatureManagementProvider.cs diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AsyncDisposeFunc.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AsyncDisposeFunc.cs new file mode 100644 index 0000000000..76e6699d23 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AsyncDisposeFunc.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Volo.Abp; + +/// +/// This class can be used to provide an action when +/// DisposeAsync method is called. +/// +public class AsyncDisposeFunc : IAsyncDisposable +{ + private readonly Func _func; + + /// + /// Creates a new object. + /// + /// func to be executed when this object is DisposeAsync. + public AsyncDisposeFunc([NotNull] Func func) + { + Check.NotNull(func, nameof(func)); + + _func = func; + } + + public async ValueTask DisposeAsync() + { + await _func(); + } +} diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/NullAsyncDisposable.cs b/framework/src/Volo.Abp.Core/Volo/Abp/NullAsyncDisposable.cs new file mode 100644 index 0000000000..1307e8764c --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/NullAsyncDisposable.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading.Tasks; + +namespace Volo.Abp; + +public sealed class NullAsyncDisposable : IAsyncDisposable +{ + public static NullAsyncDisposable Instance { get; } = new NullAsyncDisposable(); + + private NullAsyncDisposable() + { + + } + + public ValueTask DisposeAsync() + { + return default; + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs index 10c43bf7b2..68cf94d334 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs @@ -20,8 +20,8 @@ public class AbpFeatureManagementDomainModule : AbpModule options.Providers.Add(); options.Providers.Add(); - //TODO: Should be moved to the Tenant Management module - options.Providers.Add(); + //TODO: Should be moved to the Tenant Management module + options.Providers.Add(); options.ProviderPolicies[TenantFeatureValueProvider.ProviderName] = "AbpTenantManagement.Tenants.ManageFeatures"; }); diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagementProvider.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagementProvider.cs index 1cfa7012b5..809dddab2b 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagementProvider.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagementProvider.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Features; @@ -13,6 +14,11 @@ public class DefaultValueFeatureManagementProvider : IFeatureManagementProvider, return providerName == Name; } + public Task HandleContextAsync(string providerName, string providerKey) + { + return Task.FromResult(NullAsyncDisposable.Instance); + } + public virtual Task GetOrNullAsync(FeatureDefinition feature, string providerKey) { return Task.FromResult(feature.DefaultValue); diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementProvider.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementProvider.cs index 61c2347290..7998a98e60 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementProvider.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementProvider.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Volo.Abp.Features; namespace Volo.Abp.FeatureManagement; @@ -19,6 +20,11 @@ public abstract class FeatureManagementProvider : IFeatureManagementProvider return providerName == Name; } + public virtual Task HandleContextAsync(string providerName, string providerKey) + { + return Task.FromResult(NullAsyncDisposable.Instance); + } + public virtual async Task GetOrNullAsync(FeatureDefinition feature, string providerKey) { return await Store.GetOrNullAsync(feature.Name, Name, await NormalizeProviderKeyAsync(providerKey)); diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs index 9114cc4117..06c293219b 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs @@ -132,7 +132,7 @@ public class FeatureManager : IFeatureManager, ISingletonDependency var feature = FeatureDefinitionManager.Get(name); - if (feature?.ValueType?.Validator.IsValid(value) == false) + if (feature.ValueType?.Validator.IsValid(value) == false) { throw new FeatureValueInvalidException(feature.DisplayName.Localize(StringLocalizerFactory)); } @@ -149,11 +149,14 @@ public class FeatureManager : IFeatureManager, ISingletonDependency if (providers.Count > 1 && !forceToSet && value != null) { - var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, null); - if (fallbackValue.Value == value) + await using (await providers[0].HandleContextAsync(providerName, providerKey)) { - //Clear the value if it's same as it's fallback value - value = null; + var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, null); + if (fallbackValue.Value == value) + { + //Clear the value if it's same as it's fallback value + value = null; + } } } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementProvider.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementProvider.cs index 05cf79d45d..6da43abdd7 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementProvider.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementProvider.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using JetBrains.Annotations; using Volo.Abp.Features; @@ -11,6 +12,9 @@ public interface IFeatureManagementProvider //TODO: Other better method name. bool Compatible(string providerName); + //TODO: Other better method name. + Task HandleContextAsync(string providerName, string providerKey); + Task GetOrNullAsync([NotNull] FeatureDefinition feature, [CanBeNull] string providerKey); Task SetAsync([NotNull] FeatureDefinition feature, [NotNull] string value, [CanBeNull] string providerKey); diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagementProvider.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagementProvider.cs index 42e54bc1cc..7a6c8d5819 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagementProvider.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagementProvider.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Features; using Volo.Abp.MultiTenancy; @@ -19,6 +20,24 @@ public class TenantFeatureManagementProvider : FeatureManagementProvider, ITrans CurrentTenant = currentTenant; } + public override Task HandleContextAsync(string providerName, string providerKey) + { + if (providerName == Name && !providerKey.IsNullOrWhiteSpace()) + { + if (Guid.TryParse(providerKey, out var tenantId)) + { + var disposable = CurrentTenant.Change(tenantId); + return Task.FromResult(new AsyncDisposeFunc(() => + { + disposable.Dispose(); + return Task.CompletedTask; + })); + } + } + + return base.HandleContextAsync(providerName, providerKey); + } + protected override Task NormalizeProviderKeyAsync(string providerKey) { if (providerKey != null) diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs index c58a24e952..0344a67440 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs @@ -12,12 +12,14 @@ public class FeatureManager_Tests : FeatureManagementDomainTestBase private readonly IFeatureManager _featureManager; private readonly ICurrentTenant _currentTenant; private readonly IFeatureChecker _featureChecker; + private readonly IFeatureValueRepository _featureValueRepository; public FeatureManager_Tests() { _featureManager = GetRequiredService(); _featureChecker = GetRequiredService(); _currentTenant = GetRequiredService(); + _featureValueRepository = GetRequiredService(); } [Fact] @@ -153,4 +155,44 @@ public class FeatureManager_Tests : FeatureManagementDomainTestBase x.Provider.Name == EditionFeatureValueProvider.ProviderName); } + [Fact] + public async Task Test_HandleContextAsync() + { + var featureValue = await _featureValueRepository.FindAsync( + TestFeatureDefinitionProvider.EmailSupport, + TenantFeatureValueProvider.ProviderName, + TestEditionIds.TenantId.ToString() + ); + + featureValue.ShouldNotBeNull(); + featureValue.Value.ShouldBe(false.ToString().ToLower()); + + + featureValue = await _featureValueRepository.FindAsync( + TestFeatureDefinitionProvider.EmailSupport, + NextTenantFeatureManagementProvider.ProviderName, + TestEditionIds.TenantId.ToString() + ); + + featureValue.ShouldNotBeNull(); + featureValue.Value.ShouldBe(true.ToString().ToLower()); + + await _featureManager.SetAsync(TestFeatureDefinitionProvider.EmailSupport, true.ToString().ToLower(), + TenantFeatureValueProvider.ProviderName, TestEditionIds.TenantId.ToString()); + + featureValue = await _featureValueRepository.FindAsync( + TestFeatureDefinitionProvider.EmailSupport, + TenantFeatureValueProvider.ProviderName, + TestEditionIds.TenantId.ToString() + ); + featureValue.ShouldBeNull(); + + featureValue = await _featureValueRepository.FindAsync( + TestFeatureDefinitionProvider.EmailSupport, + NextTenantFeatureManagementProvider.ProviderName, + TestEditionIds.TenantId.ToString() + ); + featureValue.ShouldNotBeNull(); + featureValue.Value.ShouldBe(true.ToString().ToLower()); + } } diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBaseModule.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBaseModule.cs index 9e14de0b8c..76df8f2fba 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBaseModule.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBaseModule.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Authorization; using Volo.Abp.Autofac; using Volo.Abp.Features; @@ -29,8 +30,10 @@ public class FeatureManagementTestBaseModule : AbpModule { context.Services.Configure(options => { - //TODO: Any value can pass. After completing the permission unit test, look at it again. - options.ProviderPolicies[EditionFeatureValueProvider.ProviderName] = EditionFeatureValueProvider.ProviderName; + options.Providers.InsertBefore(typeof(TenantFeatureManagementProvider), typeof(NextTenantFeatureManagementProvider)); + + //TODO: Any value can pass. After completing the permission unit test, look at it again. + options.ProviderPolicies[EditionFeatureValueProvider.ProviderName] = EditionFeatureValueProvider.ProviderName; }); } diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestDataBuilder.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestDataBuilder.cs index d19a784f92..1685264389 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestDataBuilder.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestDataBuilder.cs @@ -23,6 +23,28 @@ public class FeatureManagementTestDataBuilder : ITransientDependency public async Task BuildAsync() { + // Tenant EmailSupport + await _featureValueRepository.InsertAsync( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.EmailSupport, + false.ToString().ToLowerInvariant(), + TenantFeatureValueProvider.ProviderName, + TestEditionIds.TenantId.ToString() + ) + ); + + // NextTenant EmailSupport + await _featureValueRepository.InsertAsync( + new FeatureValue( + _guidGenerator.Create(), + TestFeatureDefinitionProvider.EmailSupport, + true.ToString().ToLowerInvariant(), + NextTenantFeatureManagementProvider.ProviderName, + TestEditionIds.TenantId.ToString() + ) + ); + #region "Regular" edition features //SocialLogins diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/NextTenantFeatureManagementProvider.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/NextTenantFeatureManagementProvider.cs new file mode 100644 index 0000000000..acb0510521 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/NextTenantFeatureManagementProvider.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.FeatureManagement; + +public class NextTenantFeatureManagementProvider : FeatureManagementProvider, ITransientDependency +{ + public static string ProviderName => "TENANT"; + + public override string Name => ProviderName; + + protected ICurrentTenant CurrentTenant { get; } + + public NextTenantFeatureManagementProvider( + IFeatureManagementStore store, + ICurrentTenant currentTenant) + : base(store) + { + CurrentTenant = currentTenant; + } + + protected override Task NormalizeProviderKeyAsync(string providerKey) + { + if (providerKey != null) + { + return Task.FromResult(providerKey); + } + + return Task.FromResult(CurrentTenant.Id?.ToString()); + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestEditionIds.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestEditionIds.cs index ff8ce1f91f..545dbfe742 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestEditionIds.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestEditionIds.cs @@ -8,10 +8,13 @@ public static class TestEditionIds public static Guid Enterprise { get; } public static Guid Ultimate { get; } + public static Guid TenantId { get; } + static TestEditionIds() { Regular = Guid.Parse("532599ab-c0c0-4345-a04a-e322867b6e15"); Enterprise = Guid.Parse("27e50758-1feb-436a-be4f-cae8519e0cb2"); Ultimate = Guid.Parse("6ea78c22-be32-497e-aaba-a2332c564c5e"); + TenantId = Guid.Parse("8899799a-9edf-44ac-9121-310d25ef4fa1"); } } From eb7fa083356f10c5f251379093b90d3fb92bba5d Mon Sep 17 00:00:00 2001 From: William Obando Date: Thu, 10 Mar 2022 22:49:14 -0500 Subject: [PATCH 004/185] Add option to disable service bus --- .../Abp/EventBus/Azure/AbpAzureEventBusOptions.cs | 2 ++ .../Abp/EventBus/Azure/AbpEventBusAzureModule.cs | 15 +++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/framework/src/Volo.Abp.EventBus.Azure/Volo/Abp/EventBus/Azure/AbpAzureEventBusOptions.cs b/framework/src/Volo.Abp.EventBus.Azure/Volo/Abp/EventBus/Azure/AbpAzureEventBusOptions.cs index a6aa2f9857..57fa4b7f48 100644 --- a/framework/src/Volo.Abp.EventBus.Azure/Volo/Abp/EventBus/Azure/AbpAzureEventBusOptions.cs +++ b/framework/src/Volo.Abp.EventBus.Azure/Volo/Abp/EventBus/Azure/AbpAzureEventBusOptions.cs @@ -7,4 +7,6 @@ public class AbpAzureEventBusOptions public string SubscriberName { get; set; } public string TopicName { get; set; } + + public bool IsServiceBusDisabled { get; set; } } diff --git a/framework/src/Volo.Abp.EventBus.Azure/Volo/Abp/EventBus/Azure/AbpEventBusAzureModule.cs b/framework/src/Volo.Abp.EventBus.Azure/Volo/Abp/EventBus/Azure/AbpEventBusAzureModule.cs index 64c8b2fcd3..129004c871 100644 --- a/framework/src/Volo.Abp.EventBus.Azure/Volo/Abp/EventBus/Azure/AbpEventBusAzureModule.cs +++ b/framework/src/Volo.Abp.EventBus.Azure/Volo/Abp/EventBus/Azure/AbpEventBusAzureModule.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Volo.Abp.AzureServiceBus; using Volo.Abp.Modularity; @@ -19,9 +20,15 @@ public class AbpEventBusAzureModule : AbpModule public override void OnApplicationInitialization(ApplicationInitializationContext context) { - context - .ServiceProvider - .GetRequiredService() - .Initialize(); + var options = context.ServiceProvider.GetRequiredService>().Value; + + if (!options.IsServiceBusDisabled) + { + context + .ServiceProvider + .GetRequiredService() + .Initialize(); + } + } } From edf68354bc87abde35da2acd9745f49c5b0e3adf Mon Sep 17 00:00:00 2001 From: Gianpiero Caretti Date: Sat, 12 Mar 2022 01:04:54 +0100 Subject: [PATCH 005/185] Documentation fix for AddBackgroundWorkerAsync Fixed the code sample by correctly call AddBackgroundWorkerAsync instead of AddBackgroundWorker --- docs/en/Background-Workers.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/en/Background-Workers.md b/docs/en/Background-Workers.md index 0c9e26e409..726a8817ab 100644 --- a/docs/en/Background-Workers.md +++ b/docs/en/Background-Workers.md @@ -86,20 +86,20 @@ After creating a background worker class, you should add it to the `IBackgroundW [DependsOn(typeof(AbpBackgroundWorkersModule))] public class MyModule : AbpModule { - public override void OnApplicationInitialization( + public override Task OnApplicationInitializationAsync( ApplicationInitializationContext context) { - context.AddBackgroundWorker(); + context.AddBackgroundWorkerAsync(); } } ```` -`context.AddBackgroundWorker(...)` is a shortcut extension method for the expression below: +`context.AddBackgroundWorkerAsync(...)` is a shortcut extension method for the expression below: ````csharp -context.ServiceProvider +await context.ServiceProvider .GetRequiredService() - .Add( + .AddAsync( context .ServiceProvider .GetRequiredService() From b870fbe4769add75cab464791b546bad18f61af0 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 14 Mar 2022 08:54:31 +0300 Subject: [PATCH 006/185] Add code-mirror mapping for test app --- .../Volo.CmsKit.Web.Unified/abp.resourcemapping.js | 5 ++++- .../host/Volo.CmsKit.Web.Unified/package.json | 3 ++- .../cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock | 13 +++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/abp.resourcemapping.js b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/abp.resourcemapping.js index 82b2d4b649..b473302023 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/abp.resourcemapping.js +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/abp.resourcemapping.js @@ -7,6 +7,9 @@ module.exports = { "@libs" ], mappings: { - + "@node_modules/codemirror/lib/*.*": "@libs/codemirror/", + "@node_modules/codemirror/mode/**/*.*": "@libs/codemirror/mode/", + "@node_modules/codemirror/theme/**/*.*": "@libs/codemirror/theme/", + "@node_modules/codemirror/addon/**/*.*": "@libs/codemirror/addon/" } } \ No newline at end of file diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json index c27b4dc457..909a876b9d 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@abp/aspnetcore.mvc.ui.theme.basic": "^5.2.0-rc.1", - "@abp/cms-kit": "5.2.0-rc.1" + "@abp/cms-kit": "5.2.0-rc.1", + "@abp/codemirror": "~5.2.0-rc.1" } } \ No newline at end of file diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock index 47305a1cad..e69e2daced 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock @@ -90,6 +90,14 @@ "@abp/cms-kit.admin" "~5.2.0-rc.1" "@abp/cms-kit.public" "~5.2.0-rc.1" +"@abp/codemirror@~5.2.0-rc.1": + version "5.2.0-rc.1" + resolved "https://registry.yarnpkg.com/@abp/codemirror/-/codemirror-5.2.0-rc.1.tgz#0f06b4396056d1f3c854e67127d84781690d554f" + integrity sha512-iYl9LnHJTZFGUF/WJdGwXwTDUtfrodqr4RAEG+OBETe55yjGLmEW9cRjqgyZdX8UZvYGv9kSD3ScVUrv3woa/A== + dependencies: + "@abp/core" "~5.2.0-rc.1" + codemirror "^5.65.1" + "@abp/core@~5.2.0-rc.1": version "5.2.0-rc.1" resolved "https://registry.yarnpkg.com/@abp/core/-/core-5.2.0-rc.1.tgz#28dd4949d6b45a2a75f4b76fe0b78b9fa809755d" @@ -987,6 +995,11 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= +codemirror@^5.65.1: + version "5.65.2" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.2.tgz#5799a70cb3d706e10f60e267245e3a75205d3dd9" + integrity sha512-SZM4Zq7XEC8Fhroqe3LxbEEX1zUPWH1wMr5zxiBuiUF64iYOUH/JI88v4tBag8MiBS8B8gRv8O1pPXGYXQ4ErA== + collection-map@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-map/-/collection-map-1.0.0.tgz#aea0f06f8d26c780c2b75494385544b2255af18c" From 68194d7f85b736e5834aeab6700b4c97a1dcf8d1 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 14 Mar 2022 09:08:52 +0300 Subject: [PATCH 007/185] Add style & script editor for Page Create/Update --- .../Pages/CmsKit/Pages/Create.cshtml | 9 ++++++-- .../Pages/CmsKit/Pages/Update.cshtml | 5 +++++ .../Pages/CmsKit/Pages/create.js | 22 +++++++++++++++++-- .../Pages/CmsKit/Pages/update.js | 18 +++++++++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml index 841576e761..4671cb2ca2 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Create.cshtml @@ -1,6 +1,7 @@ @page @using System.Globalization +@using Volo.Abp.AspNetCore.Mvc.UI.Packages.Codemirror @using Volo.Abp.AspNetCore.Mvc.UI.Packages.TuiEditor @using Volo.Abp.AspNetCore.Mvc.UI.Packages.Uppy @using Volo.CmsKit.Admin.Web.Pages @@ -22,6 +23,9 @@ + + + @@ -31,6 +35,7 @@ @section styles { + } @@ -56,11 +61,11 @@ - + - + diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml index 1271502946..499ad1b633 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Update.cshtml @@ -1,6 +1,7 @@ @page "{Id}" @using System.Globalization +@using Volo.Abp.AspNetCore.Mvc.UI.Packages.Codemirror @using Volo.Abp.AspNetCore.Mvc.UI.Packages.TuiEditor @using Volo.Abp.AspNetCore.Mvc.UI.Packages.Uppy @using Volo.Abp.AspNetCore.Mvc.UI.Packages.Slugify @@ -23,6 +24,9 @@ + + + @@ -32,6 +36,7 @@ @section styles { + } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/create.js b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/create.js index ffe37fe5a8..1de6021a7d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/create.js +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/create.js @@ -6,22 +6,40 @@ $(function () { var $slug = $('#ViewModel_Slug'); var $buttonSubmit = $('#button-page-create'); + var scriptEditor = CodeMirror.fromTextArea(document.getElementById("ViewModel_Script"), { + mode: "javascript", + lineNumbers: true + }); + + var styleEditor = CodeMirror.fromTextArea(document.getElementById("ViewModel_Style"), { + mode: "css", + lineNumbers: true + }); + + $('.nav-tabs a').on('shown.bs.tab', function () { + scriptEditor.refresh(); + styleEditor.refresh(); + }); + $createForm.data('validator').settings.ignore = ":hidden, [contenteditable='true']:not([name]), .tui-popup-wrapper"; $createForm.on('submit', function (e) { e.preventDefault(); - + if ($createForm.valid()) { abp.ui.setBusy(); + $("#ViewModel_Style").val(styleEditor.getValue()); + $("#ViewModel_Script").val(scriptEditor.getValue()); + $createForm.ajaxSubmit({ success: function (result) { abp.notify.success(l('SuccessfullySaved')); abp.ui.clearBusy(); location.href = "../Pages"; }, - error: function(result){ + error: function (result) { abp.ui.clearBusy(); abp.notify.error(result.responseJSON.error.message); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/update.js b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/update.js index 99af38b70c..6bd90dd934 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/update.js +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/update.js @@ -7,6 +7,21 @@ $(function () { $formUpdate.data('validator').settings.ignore = ":hidden, [contenteditable='true']:not([name]), .tui-popup-wrapper"; + var scriptEditor = CodeMirror.fromTextArea(document.getElementById("ViewModel_Script"), { + mode: "javascript", + lineNumbers: true + }); + + var styleEditor = CodeMirror.fromTextArea(document.getElementById("ViewModel_Style"), { + mode: "css", + lineNumbers: true + }); + + $('.nav-tabs a').on('shown.bs.tab', function () { + scriptEditor.refresh(); + styleEditor.refresh(); + }); + $formUpdate.on('submit', function (e) { e.preventDefault(); @@ -14,6 +29,9 @@ $(function () { abp.ui.setBusy(); + $("#ViewModel_Style").val(styleEditor.getValue()); + $("#ViewModel_Script").val(scriptEditor.getValue()); + $formUpdate.ajaxSubmit({ success: function (result) { abp.notify.success(l('SuccessfullySaved')); From 880d23357af54ef7afca428dedc6bac10a089d9a Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 14 Mar 2022 09:32:21 +0300 Subject: [PATCH 008/185] Revert local changes for Volo.CmsKit.Web.Unified --- .../host/Volo.CmsKit.Web.Unified/abp.resourcemapping.js | 4 ---- modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/abp.resourcemapping.js b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/abp.resourcemapping.js index b473302023..26cdc6aad9 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/abp.resourcemapping.js +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/abp.resourcemapping.js @@ -7,9 +7,5 @@ module.exports = { "@libs" ], mappings: { - "@node_modules/codemirror/lib/*.*": "@libs/codemirror/", - "@node_modules/codemirror/mode/**/*.*": "@libs/codemirror/mode/", - "@node_modules/codemirror/theme/**/*.*": "@libs/codemirror/theme/", - "@node_modules/codemirror/addon/**/*.*": "@libs/codemirror/addon/" } } \ No newline at end of file diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json index 909a876b9d..c27b4dc457 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json @@ -4,7 +4,6 @@ "private": true, "dependencies": { "@abp/aspnetcore.mvc.ui.theme.basic": "^5.2.0-rc.1", - "@abp/cms-kit": "5.2.0-rc.1", - "@abp/codemirror": "~5.2.0-rc.1" + "@abp/cms-kit": "5.2.0-rc.1" } } \ No newline at end of file From e40dfb586e18ac522614e0180aafbdc31a36d5b4 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 14 Mar 2022 09:33:14 +0300 Subject: [PATCH 009/185] Update yarn.lock --- .../cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock index e69e2daced..47305a1cad 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock @@ -90,14 +90,6 @@ "@abp/cms-kit.admin" "~5.2.0-rc.1" "@abp/cms-kit.public" "~5.2.0-rc.1" -"@abp/codemirror@~5.2.0-rc.1": - version "5.2.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/codemirror/-/codemirror-5.2.0-rc.1.tgz#0f06b4396056d1f3c854e67127d84781690d554f" - integrity sha512-iYl9LnHJTZFGUF/WJdGwXwTDUtfrodqr4RAEG+OBETe55yjGLmEW9cRjqgyZdX8UZvYGv9kSD3ScVUrv3woa/A== - dependencies: - "@abp/core" "~5.2.0-rc.1" - codemirror "^5.65.1" - "@abp/core@~5.2.0-rc.1": version "5.2.0-rc.1" resolved "https://registry.yarnpkg.com/@abp/core/-/core-5.2.0-rc.1.tgz#28dd4949d6b45a2a75f4b76fe0b78b9fa809755d" @@ -995,11 +987,6 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -codemirror@^5.65.1: - version "5.65.2" - resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.2.tgz#5799a70cb3d706e10f60e267245e3a75205d3dd9" - integrity sha512-SZM4Zq7XEC8Fhroqe3LxbEEX1zUPWH1wMr5zxiBuiUF64iYOUH/JI88v4tBag8MiBS8B8gRv8O1pPXGYXQ4ErA== - collection-map@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-map/-/collection-map-1.0.0.tgz#aea0f06f8d26c780c2b75494385544b2255af18c" From 796fe8e158a05a1e1931f9386af794509fb20283 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 14 Mar 2022 14:36:06 +0800 Subject: [PATCH 010/185] Cache localization keys. Resolve #11887 --- ...AutoLocalizationMetadataDetailsProvider.cs | 61 +++++++++++++------ 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpDataAnnotationAutoLocalizationMetadataDetailsProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpDataAnnotationAutoLocalizationMetadataDetailsProvider.cs index 397c59a407..cb0d9547a2 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpDataAnnotationAutoLocalizationMetadataDetailsProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpDataAnnotationAutoLocalizationMetadataDetailsProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -16,14 +17,16 @@ public class AbpDataAnnotationAutoLocalizationMetadataDetailsProvider : IDisplay private readonly Lazy _stringLocalizerFactory; private readonly Lazy> _localizationOptions; + private readonly ConcurrentDictionary _displayNameKeys; public AbpDataAnnotationAutoLocalizationMetadataDetailsProvider(IServiceCollection services) { _stringLocalizerFactory = services.GetRequiredServiceLazy(); _localizationOptions = services.GetRequiredServiceLazy>(); + _displayNameKeys = new ConcurrentDictionary(); } - public void CreateDisplayMetadata(DisplayMetadataProviderContext context) + public virtual void CreateDisplayMetadata(DisplayMetadataProviderContext context) { var displayMetadata = context.DisplayMetadata; if (displayMetadata.DisplayName != null) @@ -54,36 +57,58 @@ public class AbpDataAnnotationAutoLocalizationMetadataDetailsProvider : IDisplay displayMetadata.DisplayName = () => { - /* - * DisplayName:ClassName:PropertyName - * DisplayName:PropertyName - * ClassName:PropertyName - * PropertyName - */ + var key = _displayNameKeys.GetOrAdd(context, _ => GetDisplayNameKey(context, localizer)); + return key != null ? localizer[key] : null; + }; + } + + protected virtual string GetDisplayNameKey(DisplayMetadataProviderContext context, IStringLocalizer localizer) + { + /* + * DisplayName:ClassName:PropertyName + * DisplayName:PropertyName + * ClassName:PropertyName + * PropertyName + */ - LocalizedString localizedString = null; + LocalizedString localizedString = null; - if (context.Key.ContainerType != null) + if (context.Key.ContainerType != null) + { + localizedString = localizer[PropertyLocalizationKeyPrefix + context.Key.ContainerType.Name + ":" + context.Key.Name]; + if (!localizedString.ResourceNotFound) { - localizedString = localizer[PropertyLocalizationKeyPrefix + context.Key.ContainerType.Name + ":" + context.Key.Name]; + return localizedString.Name; } + } - if (localizedString == null || localizedString.ResourceNotFound) + if (localizedString == null || localizedString.ResourceNotFound) + { + localizedString = localizer[PropertyLocalizationKeyPrefix + context.Key.Name]; + if (!localizedString.ResourceNotFound) { - localizedString = localizer[PropertyLocalizationKeyPrefix + context.Key.Name]; + return localizedString.Name; } + } - if (localizedString.ResourceNotFound && context.Key.ContainerType != null) + if (localizedString.ResourceNotFound && context.Key.ContainerType != null) + { + localizedString = localizer[context.Key.ContainerType.Name + ":" + context.Key.Name]; + if (!localizedString.ResourceNotFound) { - localizedString = localizer[context.Key.ContainerType.Name + ":" + context.Key.Name]; + return localizedString.Name; } + } - if (localizedString.ResourceNotFound) + if (localizedString.ResourceNotFound) + { + localizedString = localizer[context.Key.Name]; + if (!localizedString.ResourceNotFound) { - localizedString = localizer[context.Key.Name]; + return localizedString.Name; } + } - return localizedString; - }; + return null; } } From d703bfedae34bd110d0cd0e387335f00fdad66be Mon Sep 17 00:00:00 2001 From: mehmetuken Date: Mon, 14 Mar 2022 09:50:51 +0300 Subject: [PATCH 011/185] add imports.razor --- .../_Imports.razor | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/_Imports.razor diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/_Imports.razor b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/_Imports.razor new file mode 100644 index 0000000000..fe27745143 --- /dev/null +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/_Imports.razor @@ -0,0 +1,13 @@ +@using System.Net.Http +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.JSInterop +@using MyCompanyName.MyProjectName.Blazor.Server +@using Blazorise +@using Blazorise.DataGrid +@using Volo.Abp.BlazoriseUI +@using Volo.Abp.BlazoriseUI.Components \ No newline at end of file From d5961fe50f46aba05d239cbe8c032f2dee6ceb02 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 14 Mar 2022 12:10:30 +0300 Subject: [PATCH 012/185] Cli: Convert single back-slashes to double back-slashes in connection string resolves https://github.com/volosoft/volo/issues/9339 --- .../Building/Steps/ConnectionStringChangeStep.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs index 2292fed8e5..0f50a443e7 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs @@ -26,7 +26,7 @@ public class ConnectionStringChangeStep : ProjectBuildPipelineStep return; } - var newConnectionString = $"\"{DefaultConnectionStringKey}\": \"{context.BuildArgs.ConnectionString}\""; + var newConnectionString = $"\"{DefaultConnectionStringKey}\": \"{context.BuildArgs.ConnectionString.Replace(@"\\", @"\").Replace(@"\", @"\\")}\""; foreach (var appSettingsJson in appSettingsJsonFiles) { From d6157e632f75e5859e0b65f10cde06df2333a46c Mon Sep 17 00:00:00 2001 From: Engincan VESKE Date: Mon, 14 Mar 2022 13:14:56 +0300 Subject: [PATCH 013/185] Add new localizations for abp.io website --- .../AbpIoLocalization/Base/Localization/Resources/en.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json index fe0947da89..f772006632 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json @@ -84,7 +84,7 @@ "WouldLikeToReceiveMarketingMaterials": "I would like to receive marketing materials like product deals & special offers.", "JoinOurMarketingNewsletter": "Join our marketing newsletter", "CommunityPrivacyPolicyConfirmation": "I agree to the Terms & Conditions and Privacy Policy.", - "ABPIO-Common": "ABPIO-Common", + "WouldLikeToReceiveNotification": "I would like to receive the latest news from abp.io websites.", "CommercialNewsletterConfirmationMessage": "I agree to the Terms & Conditions and Privacy Policy.", "FreeDDDEBook": "Free DDD E-Book", "AdditionalServices": "Additional Services", @@ -118,6 +118,7 @@ "ThereIsNoEvent": "There is no event.", "Events": "Events", "Volo.AbpIo.Domain:080000": "There is already a purchase item named \"{Name}\"", - "MasteringAbpFrameworkBook": "Book: Mastering ABP Framework" + "MasteringAbpFrameworkBook": "Book: Mastering ABP Framework", + "ABPIO-CommonPreferenceDefinition": "Get latest news about ABP Platform like new posts, events and more." } } From 59a85df75f18b231517a9192bc53aa567c69aba3 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 14 Mar 2022 16:38:39 +0300 Subject: [PATCH 014/185] Add initial state of autocomplete select documentation --- docs/en/UI/AspNetCore/AutoComplete-Select.md | 43 ++++++++++++++++++ docs/en/images/abp-select2-multiple.png | Bin 0 -> 5591 bytes docs/en/images/abp-select2-single.png | Bin 0 -> 3896 bytes .../Volo.CmsKit.Web.Unified/appsettings.json | 2 +- .../Tags/Components/TagEditor/Default.cshtml | 30 +++++++----- .../Tags/Components/TagEditor/default.js | 15 ++++++ 6 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 docs/en/UI/AspNetCore/AutoComplete-Select.md create mode 100644 docs/en/images/abp-select2-multiple.png create mode 100644 docs/en/images/abp-select2-single.png diff --git a/docs/en/UI/AspNetCore/AutoComplete-Select.md b/docs/en/UI/AspNetCore/AutoComplete-Select.md new file mode 100644 index 0000000000..1a4705627d --- /dev/null +++ b/docs/en/UI/AspNetCore/AutoComplete-Select.md @@ -0,0 +1,43 @@ +# ASP.NET Core MVC / Razor Pages: Auto-Complete Select +A simple select component sometimes isn't useful with huge amount of data. ABP Provides a select implementation that works with pagination and server-side search via using [Select2](https://select2.org/). It works with single or multiple choices well. + +A screenshot can be shown below. + +| Single | Multiple | +| --- | --- | +| ![autocomplete-select-example](../../images/abp-select2-single.png) |![autocomplete-select-example](../../images/abp-select2-multiple.png) | + +## Getting Started + +This is a core feature and it's used by ABP Framework. There is no custom installation or additional package required. + +## Usage + +A simple is usage is presented below. The select must have `auto-complete-select` class and following attributes: + +- `data-autocomplete-api-url`: * API Endpoint url to get select items. **GET** request will be sent to this url. +- `data-autocomplete-display-property`: * Property name to display. _(For example: `name` ot `title`. Property name of entity/dto.)_. +- `data-autocomplete-value-property`: * Identifier property name. _(For example: `id`)_. +- `data-autocomplete-items-property`: * Property name of collection in response object. _(For example: `items`)_ +- `data-autocomplete-filter-param-name`: * Filter text property name. _(For example: `filter`)_. +- `data-autocomplete-selected-item-name`: Text to display as selected item. +- `data-autocomplete-parent-selector`: jQuery selector expression for parent DOM. _(If it's in a modal, it's suggested to send modal selector as this parameter)_. + + ```html + + ``` + + +## Possible Issues + +### Permission Restriction +If the authenticated user doesn't have permission on given url, user will get an authorization error. Be careful while designing this kind of UIs. + +You may place a lookup method in the same AppService, so your page can retrieve lookup daha of dependent entity without giving a entire read permission to users. \ No newline at end of file diff --git a/docs/en/images/abp-select2-multiple.png b/docs/en/images/abp-select2-multiple.png new file mode 100644 index 0000000000000000000000000000000000000000..bdc2d7cc95f3e2e6a0ebda0ff6f261752d4f1f29 GIT binary patch literal 5591 zcmbtYcT`hdwhve+Dj*?GL{z&${_w2LxK6~x+JHLG*ALyxHpy#BAKp+=1 zHB=2C5b7=9Igj=%@IO-XMi>xO?gr}0kkbAu%fQQNhkLsBAdnC7=g3db0Pl1z8fNYg z2tzCQqv}AuvxPt|Av9I*8F^W*O$8#1jHf%cHomB<>AYZH&cFD9_Vfkn?dJB<;`1e! znQ4>yw4y_&5TYg}vyt~L4n9N&cJZ;kO{}5XE0i54QX8za^ZXU^WHp<|{^04oml_Ac z14D>Jbu}3I;bFS_I+}a0U*`_jBF_9MK%jNfeX&1dd2?VX{UB2+M^6n14syC3;>!p~ z9v)h7S$CV6=BOZ$pdjA1Q@|^gN(2ocsKb~*ozv%euL6Sh4`l^FF#Q?g1qhxe|2~lp zC9-?V=IDKg20Yd&*N7Jzr*w_Q=^UVWcir~V+!RCS_TaDiZ@(^j`|mOlKR32FWs__xHM}q>tlt>y z=9E1efq8dN794dOrlTYuQyW(C^D)bdJM>sSo%%$Rez;xd2@{d3krm1Q3 z8Pr;a?uD`-mh41ciRj9X`}*`?HJpZ{4x(zbI|4wHLI{y>lVs(`i% z?B5mkHPwX2D&iimntJ1~lj1VWuO6y6T?QRF_`QzI;t6~)QXZeERv`)><1k!pY*gk< z3$a;>{dTi&r&gwCuH4^zrq?6c(N6D^vkbV3K;{)A;#O)4@tj1(@^yj+HSn=;$3WL_ zQAgL$E988o6Yv4z?9Vnwl1=H^n+VH0(sKM%VLguMs`dwmCJ#%~+f(|yJl_;%$ADYy zXM3tC3s{BO+NS2ZU;ze|Ui@!s)!ZZEgmSd8b`dnAW9}Tr090*_y3)Zsi}ymR>HOPx zCCRmnJDwO{KmoF>i`+y0j(m{6hMfDIXojc5ih$T$7c z9s`yO z$busnkc=-IXWpdSZw_oE)-R|;l5ddA8|YdML=4egKpEbJGR0u_$$WI|k;DK`{iq~N z>vkzREQ`-3g8}gQUA<&ag}lsY+w+Q%f4Ly4QGx$L;bClx+YD0MFm3p?0jAz|@R%-7 znl4%`1D3xajH4)R)fTQ0lS@zDU#Vb}N5lqva+)0tnGF|4qXpxaTRJk?7sd|LKV#=F z98b7grc7<;6sIT8d4oCoy2?xGJqakK9Ghd9Pt`MQZe%3Mjsy~alpzW_YJ?3k-{iK# zOr1S4G#`n+M*uAH?Kp4klXeOTgf^L=B9aCJOf@H>>Z zfUWUjQ-6Db>U-`8M%rx$4b+aentm7TF#S^Ab~F9bWIZXn&9cd-{mGI%7{|yeZ@{BL z9kl%VeN=9+U+|T~{Zr%C)MOsv28aBVNmsnPb<#~q<4{Xa%~(S%_q}1hn-5LTY&AK3 zq1qpCv-)H`*dj$iQ2z=G%$=EC{DPI}UU$3nomrAJ$ZqDm&#D8G~<8 zz>I$D%1q*fX|YghSzZsEtSXMLO|5;_@+pm$`a`3(KDMM|ro0EO#_jh@Opi|Iy(D&2 zk$IdY5GoFD)CBB8eBKYR;Q~-OQ&-_m+oZJJ@*9~vM6tYstRe2(G>RiWT6*KH&auA3 z!rp^+viZ}BIaeDOs))F7Fh5p@{B40G*I=T;cz&F7%GMrhHxFv(phaxE1^&y0omu<2 zLY{QDZuO*HywCNf2lQ)C?;~`N`orXG9}Wt7i`Kc|zO&U5pxN)kE+TFh$56G|r_C7!P;IK`-W;nk5z1EP`Z z*2o)-{htZ>w(W+8pgr57F%>fU+?q%^0fIc@=bSGSOVNb0;~wL3?d?9&FDx{n#GZ`g zi92?ac4OAX1O|}D$qM=G4N8jFWF0s1KkX0URNIYL10GS{RQrhrnz4Q3PP*MhGTxgr zy4-*6+zXo+(MJ{{^+nto(+1nF`0ph6-jQxz(O-aTNOs8WOTOtJ0O%^rG%+<5N*2<` ziJkm(;wN2QT_~#V_=Mu)nh3PWc*Jcb;>h{tPUo#&89fC7WQoT4U7x@`A*1W@zt*Lb z{nqBETy&nv9pO9je98)Ef{$d9JnBAptW5~m+1cS1Uag;iaB7-~sDSaPDEwwCP_}W~ zY9&Gjc(B~{_y>WRmu_Rca_fP(Wy3SrW;7*+7D6ZP{YB9sITZbCe0g2^@xKsQC1K#Eh4Nd6@2H@G zo}NlPV4Ai~%5jwWzRgVtf020(i0}l+4Xp!=F7XFX;s+ z*6Pa2eBVHz4Gb88RG>~`Pf{u>BnjQ;7r@2*{~6vts3FU1rW1{Wz|O-c1vH%C|G6Pv z#J?w59q}8bv2NUR9ZOw~BJ+*1MbX;qOK2Xl@&_lH5Qa^GF^}$*@vhPvobUUEM#G`we#K$O5AYXRMyjRj03{$f5!rpGNhthuglZyF+m6f`cXh zGx0%zb-WZD+1LVFLRtnjtR)Ra&H{odnGd-mUQTI$`<|^Qu~9G zFx*(f?=DwELt8h$;OR5ZL~`0jS-#_Za+dv}lY>N;3N~YvU(pJFWZ;jG3$aabQum&b z1J{;%gd`WFq^6FW*!!U)$0!qTl${MUb=@p#$Jf-(cyR?RVa~jTcFqh$P2Q}{MB?Y%-O)UOphu3@o@A*%;~tCramx85`JW!}ZcDq!vUX*b5(Q zWZ*}0YG==-zD4RbJ=saT$;*)JqiX)LIt(p6JkqW}sJvhc2olB1 z%wte;7^Az;H!mAFnM8HX#y~Q%!*!zEK;NTo%%ne=*G|;>$G!hklN3O|{Tr z`)xYMaSDp$;hCOura#ML_cAG-{)cdN!xio%SefP@M*Lbbw7}5|DTOo&8hJA5i|5^} zoF9KMdsMJnekY%GQKR7e*M*yh>KNwg;48&vId(zjPfH(IfM#*1#OFyzp#=u+8y1kW z6niyn!b?1x_#H;F=R60kV2WjDFD;%1w0Um%m0pTW8x|#{e0>HJ&y_%Ly_)v&PkbQx z!&=+c0QnT|xG2C69V&yCkR&m|m~C*#e%JMTWU#xgTPS;u&1 zax%5$d2!#aTHQoqC&sTq+s;xmEPdQDNh!@h7zNH*l87KN4$ADb&)T>A4Q7z9wQmB5 zbb#$3=)bpT|4Cn$b4@*NXTKv8gTd^75CU^YvlFiv%MG2zNwPdgA~#$kW*WSuem8@i zR9tfoeHc3!Iwu`W*gu;?v$P?w>e;{;5F;Za5B0#!NB;I-bN2rO7vrmTjpGw9j?f^$ z9@8|+l5R`zeB9M*KvTg`hA-;Hg7Bl94m-ZTES+y*7{7COB zzoYBFr&GfB@q|1<1zi&*lN-6m~+czneAalceDi*-Oo4mS6rv}0@u z?aYqs3ux${u^fVl+s-7{PjYZ^V7O#~@W$AmZ=h7Pv&HV9j z)^GMSq42C^Q=ncHOHb6}m4#GJ*QP+&(o9Y6=xNUa^Xng2yABXHZj9WWedbQYUKq>n zGB=f=wp=~*-F7H5J{8G26Y*~Ez>02TNbF{_!!}6Xn)NB6`}mI{JD(jwx%4!3e~fw> zrVM*|W}pQLFW%`-pxU{1-%9nN`_2n(=>6!$b;NRJ+Aq$e^r~8UX1<#g>(CQqfHVZI zGI7W}o(fg({IlSifRGndh|zaC5V&wyv4SmG{b`(EM0Jly>W%RY)j2t)71rCg|DjXK z84`09epYE3d5<}dd_OmOueJ;W0WiZwU`woRgYdA3LOSh?x-qgWw_Qy3^pqJnQ@MaL zOsW_)#LdwisZUAur3s0?4V!3veX7E|X>Zd5ebsjg^`@Oq{^`r=Lxm`2rEBikB52d~ zf?UDd`!}iZZWtZuS*63iu<35Y+-=i-L|v`3{H!eXb%w!ZoZwfS;`G8*Q2xI z`9qvMA?qHi8csiHg>m8j0*|pxLIuI0HBYa#J|||EZ5*fXh*2>1_S3zY1OrZ%QKuRt zMW@#!(|~tqEvB*7S=^{})p{TRGac{d)4%B_T!`glw)uW#2a!jh!(WD9@Z_IU|hFEppNhBK9Ux-Z}6 z7nB&*xi^-*nv~QnVrAbfWoKVy@pL~()HAitT(A-%>)pY5yeHu)R)-p8xk7lZ4M&u* ztYht92#;yZPRtZt2R=G(x?A#xS}UFPIcvIYAWVPpJ>F^2I@Y~XX-kiseBW?Z{bRp< zGmejx@+O{Z}4;{IdjoOPBKi>P|C*SY6e@L1^%<{5LG_DmJS`+>mNq`;E!X z-2dxE-hXmZn1$|d%s)OjTrK}9jVUxZCJ1vaJMa#eP2I`$x7YajUf`W7hme;Z)4 z5Hn@`nGoK7CCODTJ=IZpZ5O<^flLhwNc)@xZebc46af8V6$Q5+GdG$l)q%0Q_TPS! dds^1S6B=C&AEMMa_hWF9rkb8=sq&MTe*=B&An5=A literal 0 HcmV?d00001 diff --git a/docs/en/images/abp-select2-single.png b/docs/en/images/abp-select2-single.png new file mode 100644 index 0000000000000000000000000000000000000000..219355dd661a7bf780e0f044616715c24f4ada5b GIT binary patch literal 3896 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 L -
+ -
- - -
+
+ + + @**@ +
- @if (Model.DisplaySubmitButton) - { - - - - } -
\ No newline at end of file + @if (Model.DisplaySubmitButton) + { + + + + } + \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Tags/Components/TagEditor/default.js b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Tags/Components/TagEditor/default.js index 834aedf3d6..e3607d48fd 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Tags/Components/TagEditor/default.js +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Tags/Components/TagEditor/default.js @@ -1,5 +1,20 @@ $(function () { + var $selectTags = $("#tags"); + + function initSelectTagEditor() { + $selectTags.data('autocompleteApiUrl', '/api/cms-kit-admin/tags'); + $selectTags.data('autocompleteDisplayProperty', 'name'); + $selectTags.data('autocompleteValueProperty', 'id'); + $selectTags.data('autocompleteItemsProperty', 'items'); + $selectTags.data('autocompleteFilterParamName', 'filter'); + + abp.dom.initializers.initializeAutocompleteSelects($selectTags); + } + + initSelectTagEditor(); + + var $tagEditorForms = $('.tag-editor-form'); $tagEditorForms.on('submit', function (e) { From cce1be21ca29de53a879647c56df624ca71f818d Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Mon, 14 Mar 2022 16:38:51 +0300 Subject: [PATCH 015/185] Update AutoComplete-Select.md --- docs/en/UI/AspNetCore/AutoComplete-Select.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/en/UI/AspNetCore/AutoComplete-Select.md b/docs/en/UI/AspNetCore/AutoComplete-Select.md index 1a4705627d..0fbeb4c6ae 100644 --- a/docs/en/UI/AspNetCore/AutoComplete-Select.md +++ b/docs/en/UI/AspNetCore/AutoComplete-Select.md @@ -31,6 +31,8 @@ A simple is usage is presented below. The select must have `auto-complete-select data-autocomplete-value-property="id" data-autocomplete-items-property="items" data-autocomplete-filter-param-name="filter"> + +