diff --git a/docs/en/modules/setting-management.md b/docs/en/modules/setting-management.md index dcb021fe14..6943316296 100644 --- a/docs/en/modules/setting-management.md +++ b/docs/en/modules/setting-management.md @@ -74,6 +74,10 @@ namespace Demo So, you can get or set a setting value for different setting value providers (Default, Global, User, Tenant... etc). +The scoped `GetOrNull...` and `GetAll...` extension methods use fallback values by default. Pass `fallback: false` when you need only the value explicitly stored for the requested provider. Setting a value to `null` clears that provider's value. + +For a non-encrypted inherited setting, setting a provider value to the same value as its fallback also clears the provider record by default. The comparison is case-insensitive. Pass `forceToSet: true` to a user- or tenant-scoped `Set...` method when you intentionally need to persist the same value as the fallback. `SetForTenantOrGlobalAsync` honors this parameter only when `tenantId` has a value; its global branch calls `SetGlobalAsync`, which does not expose the parameter. For encrypted settings, fallback-equal clearing does not apply to non-empty values because they are encrypted before comparison; an empty string can still be cleared when it equals the fallback. Pass `null` explicitly when you want to clear the provider value. + > Use the `ISettingProvider` instead of the `ISettingManager` if you only need to read the setting values, because it implements caching and supports all deployment scenarios. You can use the `ISettingManager` if you are creating a setting management UI. ### Setting Cache @@ -88,9 +92,9 @@ Setting Management module is extensible, just like the [setting system](../frame * `ConfigurationSettingManagementProvider`: Gets the value from the [IConfiguration service](../framework/fundamentals/configuration.md). It can not set the configuration value because it is not possible to change the configuration values on runtime. * `GlobalSettingManagementProvider`: Gets or sets the global (system-wide) value for a setting. * `TenantSettingManagementProvider`: Gets or sets the setting value for a tenant. -* `UserSettingManagementProvider`: Gets the setting value for a user. +* `UserSettingManagementProvider`: Gets or sets the setting value for a user. -`ISettingManager` uses the setting management providers on get/set methods. Typically, every setting management provider defines extension methods on the `ISettingManagement` service (like `SetForUserAsync` defined by the user setting management provider). +`ISettingManager` uses the setting management providers on get/set methods. Typically, every setting management provider defines extension methods on the `ISettingManager` service (like `SetForUserAsync` defined by the user setting management provider). If you want to create your own provider, implement the `ISettingManagementProvider` interface or inherit from the `SettingManagementProvider` base class: @@ -119,23 +123,43 @@ Configure(options => The order of the providers are important. Providers are executed in the reverse order. That means the `CustomSettingProvider` is executed first for this example. You can insert your provider in any order in the `Providers` list. -## See Also +## Dynamic Setting Definitions -* [Settings](../framework/infrastructure/settings.md) +The module can persist setting definitions in addition to setting values. `SettingManagementOptions` controls this behavior: + +* `SaveStaticSettingsToDatabase` is `true` by default. During application initialization, definitions contributed by code are synchronized with the `SettingDefinitions` table or collection (`AbpSettingDefinitions` by default). +* `IsDynamicSettingStoreEnabled` is `false` by default. Enable it to include persisted definitions in the setting definition store at runtime. + +```csharp +Configure(options => +{ + options.IsDynamicSettingStoreEnabled = true; +}); +``` + +Both options are disabled automatically in an ABP data migration environment. Static definitions continue to come from the application's setting definition providers when the dynamic store is disabled. ## Setting Management UI -Setting Mangement module provided the Email setting, Feature management and Timezone setting UI by default. +The MVC, Blazor and MudBlazor packages provide built-in Email and Time Zone setting groups. The Angular configuration package provides the built-in Email group. Other modules can contribute their own groups to the same page. For example, the Feature Management module contributes the Feature Management group shown below. ![EmailSettingUi](../images/setting-management-email-ui.png) > You can click the Send test email button to send a test email to check your email settings. +The Email group uses `IEmailSettingsAppService`. Reading and updating the settings requires the `SettingManagement.Emailing` permission. Sending a test email additionally requires `SettingManagement.Emailing.Test`. The operations are exposed under `/api/setting-management/emailing`, with the test operation at `/api/setting-management/emailing/send-test-email`. + +Email operations also require the `SettingManagement.Enable` feature, which is `true` by default. For tenants, they additionally require its child feature, `SettingManagement.AllowChangingEmailSettings`, which is `false` by default. The read operation does not return the stored SMTP password. On update, a blank password leaves the existing password unchanged. + ![FeatureManagementUi](../images/setting-management-feature-management-ui.png) ![TimeZoneSettingUi](../images/setting-management-time-zone-ui.png) -Setting it is extensible; You can add your tabs to this page for your application settings. +The Time Zone group uses `ITimeZoneSettingsAppService` and requires the `SettingManagement.TimeZone` permission. Its HTTP API is exposed at `/api/setting-management/timezone`; `GET /api/setting-management/timezone/timezones` returns the available IANA time zones. Updating the value to `Unspecified` clears the host-global or current-tenant value so the configured fallback is used. + +The built-in MVC, Blazor and MudBlazor contributors add this group only when `IClock.SupportsMultipleTimezone` is `true`. + +The page is extensible, so you can add groups for your application's settings. ### MVC UI @@ -180,9 +204,14 @@ Create a `BookStoreSettingPageContributor.cs` file under the `Settings` folder: The content of the file is shown below: ```csharp -public class BookStoreSettingPageContributor : ISettingPageContributor +public class BookStoreSettingPageContributor : SettingPageContributorBase { - public Task ConfigureAsync(SettingPageCreationContext context) + public BookStoreSettingPageContributor() + { + RequiredPermissions("BookStore.Settings"); + } + + public override Task ConfigureAsync(SettingPageCreationContext context) { context.Groups.Add( new SettingPageGroup( @@ -195,15 +224,13 @@ public class BookStoreSettingPageContributor : ISettingPageContributor return Task.CompletedTask; } - - public Task CheckPermissionsAsync(SettingPageCreationContext context) - { - // You can check the permissions here - return Task.FromResult(true); - } } ``` +Derive from `SettingPageContributorBase` instead of directly implementing `ISettingPageContributor`. Use `RequiredPermissions`, `RequiredFeatures` and `RequiredTenantSideFeatures` in the constructor to declare the conditions for the group. The module batches these checks before calling `ConfigureAsync`. + +`SettingPageGroup` also accepts an optional `parameter` for the view component. Groups are ordered by `order`, then by display name. + Open the `BookStoreWebModule.cs` file and add the following code: ```csharp @@ -246,8 +273,13 @@ The content of the file is shown below: ```csharp public class BookStoreSettingComponentContributor : ISettingComponentContributor { - public Task ConfigureAsync(SettingComponentCreationContext context) + public async Task ConfigureAsync(SettingComponentCreationContext context) { + if (!await CheckPermissionsAsync(context)) + { + return; + } + context.Groups.Add( new SettingComponentGroup( "Volo.Abp.MySettingGroup", @@ -256,18 +288,22 @@ public class BookStoreSettingComponentContributor : ISettingComponentContributor order : 1 ) ); - - return Task.CompletedTask; } - public Task CheckPermissionsAsync(SettingComponentCreationContext context) + public async Task CheckPermissionsAsync(SettingComponentCreationContext context) { - // You can check the permissions here - return Task.FromResult(true); + var authorizationService = context.ServiceProvider + .GetRequiredService(); + + return await authorizationService.IsGrantedAsync("BookStore.Settings"); } } ``` +The settings page calls `ConfigureAsync` for every registered contributor, while menu visibility is checked separately with `CheckPermissionsAsync`. Perform the authorization check before adding the group, as in the example, and return the same result from `CheckPermissionsAsync`. + +MudBlazor provides the equivalent `ISettingComponentContributor`, `SettingComponentCreationContext`, `SettingComponentGroup` and `SettingManagementComponentOptions` types in the `Volo.Abp.SettingManagement.Blazor.MudBlazor` namespace. Use those types for a MudBlazor UI. In both Blazor UI stacks, a group accepts an optional component `parameter` and is ordered by `order`, then by display name. + Open the `BookStoreBlazorModule.cs` file and add the following code: ```csharp @@ -293,34 +329,72 @@ Create a component with the following command: yarn ng generate component my-settings ``` -Open the `app.component.ts` and modify the file as shown below: +Register the module configuration and your setting tab in `app.config.ts`: -```js -import { Component, inject } from '@angular/core'; -import { SettingTabsService } from '@abp/ng.setting-management/config'; +```ts +import { ApplicationConfig, inject, provideAppInitializer } from '@angular/core'; +import { + provideSettingManagementConfig, + SettingTabsService, +} from '@abp/ng.setting-management/config'; import { MySettingsComponent } from './my-settings/my-settings.component'; -@Component({ - // component metadata -}) -export class AppComponent { - private readonly settingTabs = inject(SettingTabsService); - - constructor() { - this.settingTabs.add([ - { - name: 'MySettings', - order: 1, - requiredPolicy: 'policy key here', - component: MySettingsComponent, - }, - ]); - } +function configureSettingTabs() { + const settingTabs = inject(SettingTabsService); + settingTabs.add([ + { + name: 'MySettings', + order: 1, + requiredPolicy: 'BookStore.Settings', + component: MySettingsComponent, + }, + ]); } + +export const appConfig: ApplicationConfig = { + providers: [ + provideSettingManagementConfig(), + provideAppInitializer(configureSettingTabs), + ], +}; ``` +`provideSettingManagementConfig` registers the route, the built-in Email tab and route visibility. The Administration -> Settings route is visible only when it has at least one visible tab and the `SettingManagement.Enable` feature is enabled. A tab can use `requiredPolicy` and `invisible` to control its own visibility. + +The `@abp/ng.setting-management` package exports the `SettingManagementComponent`, `createRoutes` and the `eSettingManagementComponents.SettingManagement` key. Use the key with the [component replacement system](../framework/ui/angular/component-replacement.md) when you need to replace the complete Setting Management page. `SettingManagementModule.forLazy` and `SettingManagementConfigModule.forRoot` are obsolete; use `createRoutes` and `provideSettingManagementConfig` in standalone applications. + #### Run the Application Navigate to `/setting-management` route to see the changes: ![Custom Settings Tab](../images/custom-settings.png) + +## Database Providers + +The Entity Framework Core and MongoDB packages persist setting values and setting definition records. + +### Common + +`AbpSettingManagementDbProperties` exposes the common persistence configuration: + +* `DbTablePrefix` is the prefix used for EF Core tables and MongoDB collections. It defaults to `AbpCommonDbProperties.DbTablePrefix`. +* `DbSchema` is the schema used by EF Core. MongoDB does not use this value. +* `ConnectionStringName` is `AbpSettingManagement`. Define this named connection string to place the module in a separate database; otherwise, it falls back to the `Default` connection string. + +### Entity Framework Core + +The Entity Framework Core provider maps the following tables with the default prefix: + +* `AbpSettings` +* `AbpSettingDefinitions` + +### MongoDB + +The MongoDB provider maps the following collections with the default prefix: + +* `AbpSettings` +* `AbpSettingDefinitions` + +## See Also + +* [Settings](../framework/infrastructure/settings.md)