mirror of https://github.com/abpframework/abp.git
8 changed files with 476 additions and 0 deletions
@ -0,0 +1,12 @@ |
|||
using Volo.Abp.AspNetCore.Components; |
|||
using Volo.Abp.FeatureManagement.Localization; |
|||
|
|||
namespace Volo.Abp.FeatureManagement.Blazor.MudBlazor; |
|||
|
|||
public abstract class AbpFeatureManagementComponentBase : AbpComponentBase |
|||
{ |
|||
protected AbpFeatureManagementComponentBase() |
|||
{ |
|||
LocalizationResource = typeof(AbpFeatureManagementResource); |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
@using Volo.Abp.Validation.StringValues |
|||
@using Microsoft.Extensions.Localization |
|||
@using MudBlazor |
|||
@using Volo.Abp.FeatureManagement.Localization |
|||
@inherits AbpFeatureManagementComponentBase |
|||
|
|||
<MudDialog @bind-IsVisible="@_isVisible" Options="@(new DialogOptions { MaxWidth = MaxWidth.Large, FullWidth = true })" CloseOnEscapeKey="true"> |
|||
<TitleContent> |
|||
<MudText Typo="Typo.h6">@L["Features"]@ProviderKeyDisplayName</MudText> |
|||
</TitleContent> |
|||
<DialogContent> |
|||
@if (Groups == null || !Groups.Any()) |
|||
{ |
|||
<MudText Class="m-3">@L["NoFeatureFoundMessage"]</MudText> |
|||
} |
|||
else |
|||
{ |
|||
<MudTabs @bind-ActivePanelIndex="@_activeTabIndex" |
|||
Variant="Variant.Pills" |
|||
Position="Position.Left" |
|||
PanelClass="pa-4"> |
|||
@foreach (var group in Groups) |
|||
{ |
|||
<MudTabPanel Text="@group.DisplayName"> |
|||
<MudText Typo="Typo.h6" Class="mb-3">@group.DisplayName</MudText> |
|||
<MudDivider Class="my-2" /> |
|||
@foreach (var feature in group.Features) |
|||
{ |
|||
var disabled = IsDisabled(feature); |
|||
<div class="mt-2" style="@GetFeatureStyles(feature)"> |
|||
@if (feature.ValueType is FreeTextStringValueType) |
|||
{ |
|||
<MudTextField Label="@GetShownName(feature)" |
|||
Disabled="@disabled" |
|||
Value="@feature.Value" |
|||
ValueChanged="@(async (v) => await OnFeatureValueChangedAsync(v, feature))" |
|||
Variant="Variant.Outlined" /> |
|||
@if (feature.Description != null) |
|||
{ |
|||
<MudText Typo="Typo.caption" Class="mt-1">@feature.Description</MudText> |
|||
} |
|||
} |
|||
|
|||
@if (feature.ValueType is SelectionStringValueType) |
|||
{ |
|||
var items = ((SelectionStringValueType)feature.ValueType).ItemSource.Items; |
|||
var selectedValue = SelectionStringValues[feature.Name]; |
|||
<MudSelect T="string" |
|||
Label="@GetShownName(feature)" |
|||
Disabled="@disabled" |
|||
Value="@selectedValue" |
|||
ValueChanged="@(s => SelectionStringValues[feature.Name] = s)" |
|||
Variant="Variant.Outlined"> |
|||
@foreach (var item in items) |
|||
{ |
|||
<MudSelectItem Value="@item.Value"> |
|||
@CreateStringLocalizer(item.DisplayText.ResourceName).GetString(item.DisplayText.Name) |
|||
</MudSelectItem> |
|||
} |
|||
</MudSelect> |
|||
@if (feature.Description != null) |
|||
{ |
|||
<MudText Typo="Typo.caption" Class="mt-1">@feature.Description</MudText> |
|||
} |
|||
} |
|||
|
|||
@if (feature.ValueType is ToggleStringValueType) |
|||
{ |
|||
<MudCheckBox Disabled="@disabled" |
|||
Checked="@ToggleValues[feature.Name]" |
|||
CheckedChanged="@(async (v) => await OnSelectedValueChangedAsync(v, feature))" |
|||
Label="@GetShownName(feature)" /> |
|||
@if (feature.Description != null) |
|||
{ |
|||
<MudText Typo="Typo.caption" Class="mt-1">@feature.Description</MudText> |
|||
} |
|||
} |
|||
</div> |
|||
} |
|||
</MudTabPanel> |
|||
} |
|||
</MudTabs> |
|||
} |
|||
</DialogContent> |
|||
<DialogActions> |
|||
<MudButton OnClick="@CloseModal">@L["Cancel"]</MudButton> |
|||
<MudButton Color="Color.Primary" Variant="Variant.Text" OnClick="@(async () => await DeleteAsync(ProviderName, ProviderKey))">@L["ResetToDefault"]</MudButton> |
|||
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="@SaveAsync">@L["Save"]</MudButton> |
|||
</DialogActions> |
|||
</MudDialog> |
|||
@ -0,0 +1,263 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Components; |
|||
using Microsoft.Extensions.Localization; |
|||
using Microsoft.Extensions.Options; |
|||
using MudBlazor; |
|||
using Volo.Abp.AspNetCore.Components.Messages; |
|||
using Volo.Abp.AspNetCore.Components.Web.Configuration; |
|||
using Volo.Abp.FeatureManagement.Blazor.MudBlazor; |
|||
using Volo.Abp.Features; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Validation.StringValues; |
|||
|
|||
namespace Volo.Abp.FeatureManagement.Blazor.MudBlazor.Components; |
|||
|
|||
public partial class FeatureManagementModal |
|||
{ |
|||
[Inject] protected IFeatureAppService FeatureAppService { get; set; } = default!; |
|||
|
|||
[Inject] protected IUiMessageService UiMessageService { get; set; } = default!; |
|||
|
|||
[Inject] protected IStringLocalizerFactory HtmlLocalizerFactory { get; set; } = default!; |
|||
|
|||
[Inject] protected IOptions<AbpLocalizationOptions> LocalizationOptions { get; set; } = default!; |
|||
|
|||
[Inject] protected ICurrentApplicationConfigurationCacheResetService CurrentApplicationConfigurationCacheResetService { get; set; } = default!; |
|||
|
|||
[Inject] protected ISnackbar Snackbar { get; set; } = default!; |
|||
|
|||
[Inject] protected IDialogService DialogService { get; set; } = default!; |
|||
|
|||
protected bool _isVisible; |
|||
protected int _activeTabIndex = 0; |
|||
|
|||
protected string? ProviderName; |
|||
protected string? ProviderKey; |
|||
protected string? ProviderKeyDisplayName; |
|||
|
|||
protected List<FeatureGroupDto>? Groups { get; set; } |
|||
|
|||
protected Dictionary<string, bool> ToggleValues = new(); |
|||
|
|||
protected Dictionary<string, string> SelectionStringValues = new(); |
|||
|
|||
public virtual async Task OpenAsync(string providerName, string? providerKey = null, string? providerKeyDisplayName = null) |
|||
{ |
|||
try |
|||
{ |
|||
ProviderName = providerName; |
|||
ProviderKey = providerKey; |
|||
|
|||
ToggleValues = new Dictionary<string, bool>(); |
|||
SelectionStringValues = new Dictionary<string, string>(); |
|||
|
|||
if (!providerKeyDisplayName.IsNullOrWhiteSpace()) |
|||
{ |
|||
ProviderKeyDisplayName = $" - {providerKeyDisplayName}"; |
|||
} |
|||
else |
|||
{ |
|||
ProviderKeyDisplayName = string.Empty; |
|||
} |
|||
|
|||
var result = await FeatureAppService.GetAsync(ProviderName, ProviderKey); |
|||
|
|||
Groups = result?.Groups ?? new List<FeatureGroupDto>(); |
|||
|
|||
if (Groups.Any()) |
|||
{ |
|||
_activeTabIndex = 0; |
|||
} |
|||
|
|||
foreach (var featureGroupDto in Groups) |
|||
{ |
|||
foreach (var featureDto in featureGroupDto.Features) |
|||
{ |
|||
if (featureDto.ValueType is ToggleStringValueType) |
|||
{ |
|||
ToggleValues.Add(featureDto.Name, bool.Parse(featureDto.Value)); |
|||
} |
|||
|
|||
if (featureDto.ValueType is SelectionStringValueType) |
|||
{ |
|||
SelectionStringValues.Add(featureDto.Name, featureDto.Value); |
|||
} |
|||
} |
|||
} |
|||
|
|||
_isVisible = true; |
|||
await InvokeAsync(StateHasChanged); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
await HandleErrorAsync(ex); |
|||
} |
|||
} |
|||
|
|||
public virtual Task CloseModal() |
|||
{ |
|||
_isVisible = false; |
|||
return InvokeAsync(StateHasChanged); |
|||
} |
|||
|
|||
protected virtual async Task SaveAsync() |
|||
{ |
|||
try |
|||
{ |
|||
if (Groups == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var features = new UpdateFeaturesDto |
|||
{ |
|||
Features = Groups.SelectMany(g => g.Features).Select(f => new UpdateFeatureDto |
|||
{ |
|||
Name = f.Name, |
|||
Value = f.ValueType is ToggleStringValueType ? ToggleValues[f.Name].ToString() : |
|||
f.ValueType is SelectionStringValueType ? SelectionStringValues[f.Name] : f.Value |
|||
}).ToList() |
|||
}; |
|||
|
|||
await FeatureAppService.UpdateAsync(ProviderName!, ProviderKey, features); |
|||
|
|||
await CurrentApplicationConfigurationCacheResetService.ResetAsync(); |
|||
|
|||
_isVisible = false; |
|||
await InvokeAsync(StateHasChanged); |
|||
Snackbar.Add(L["SavedSuccessfully"], Severity.Success); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
await HandleErrorAsync(ex); |
|||
} |
|||
} |
|||
|
|||
public virtual async Task DeleteAsync(string providerName, string? providerKey = null) |
|||
{ |
|||
try |
|||
{ |
|||
var confirmed = await DialogService.ShowMessageBox( |
|||
L["AreYouSure"], |
|||
L["AreYouSureToResetToDefault"], |
|||
yesText: L["Yes"], |
|||
cancelText: L["Cancel"]); |
|||
|
|||
if (confirmed != true) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
await FeatureAppService.DeleteAsync(ProviderName!, ProviderKey); |
|||
Snackbar.Add(L["ResetedToDefault"], Severity.Success); |
|||
|
|||
await CloseModal(); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
await HandleErrorAsync(ex); |
|||
} |
|||
} |
|||
|
|||
protected virtual string GetNormalizedGroupName(string name) |
|||
{ |
|||
return "FeatureGroup_" + name.Replace(".", "_"); |
|||
} |
|||
|
|||
protected virtual string GetFeatureStyles(FeatureDto feature) |
|||
{ |
|||
return $"margin-left: {feature.Depth * 20}px"; |
|||
} |
|||
|
|||
protected virtual bool IsDisabled(FeatureDto feature) |
|||
{ |
|||
return feature.Value != null && |
|||
feature.Provider.Name != null && |
|||
feature.Provider.Name != ProviderName && |
|||
feature.Provider.Name != DefaultValueFeatureValueProvider.ProviderName; |
|||
} |
|||
|
|||
public virtual string GetShownName(FeatureDto featureDto) |
|||
{ |
|||
return !IsDisabled(featureDto) |
|||
? featureDto.DisplayName |
|||
: $"{featureDto.DisplayName} ({featureDto.Provider.Name})"; |
|||
} |
|||
|
|||
protected virtual async Task OnFeatureValueChangedAsync(string? value, FeatureDto feature) |
|||
{ |
|||
if (feature?.ValueType?.Validator.IsValid(value) == true) |
|||
{ |
|||
feature.Value = value; |
|||
} |
|||
else |
|||
{ |
|||
await UiMessageService.Warn(L["Volo.Abp.FeatureManagement:InvalidFeatureValue", feature.DisplayName]); |
|||
} |
|||
} |
|||
|
|||
protected virtual Task OnSelectedValueChangedAsync(bool value, FeatureDto feature) |
|||
{ |
|||
ToggleValues[feature.Name] = value; |
|||
|
|||
if (value) |
|||
{ |
|||
CheckParents(feature.ParentName); |
|||
} |
|||
else |
|||
{ |
|||
UncheckChildren(feature.Name); |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
protected virtual void CheckParents(string? parentName) |
|||
{ |
|||
if (parentName.IsNullOrWhiteSpace() || Groups == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var featureGroupDto in Groups) |
|||
{ |
|||
foreach (var featureDto in featureGroupDto.Features) |
|||
{ |
|||
if (featureDto.Name == parentName && ToggleValues.ContainsKey(featureDto.Name)) |
|||
{ |
|||
ToggleValues[featureDto.Name] = true; |
|||
CheckParents(featureDto.ParentName); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected virtual void UncheckChildren(string featureName) |
|||
{ |
|||
if (Groups == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var featureGroupDto in Groups) |
|||
{ |
|||
foreach (var featureDto in featureGroupDto.Features) |
|||
{ |
|||
if (featureDto.ParentName == featureName && ToggleValues.ContainsKey(featureDto.Name)) |
|||
{ |
|||
ToggleValues[featureDto.Name] = false; |
|||
UncheckChildren(featureDto.Name); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected virtual IStringLocalizer CreateStringLocalizer(string resourceName) |
|||
{ |
|||
return HtmlLocalizerFactory.CreateByResourceNameOrNull(resourceName) ?? |
|||
HtmlLocalizerFactory.CreateDefaultOrNull(); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
@using MudBlazor |
|||
@using Volo.Abp.FeatureManagement.Localization |
|||
@inherits Volo.Abp.AspNetCore.Components.AbpComponentBase |
|||
|
|||
@if (Settings != null && Settings.HasManageHostFeaturesPermission) |
|||
{ |
|||
<MudForm> |
|||
<MudText Typo="Typo.body1" Class="mb-3">@L["ManageHostFeaturesText"]</MudText> |
|||
<MudButton Size="Size.Small" |
|||
Color="Color.Primary" |
|||
Variant="Variant.Filled" |
|||
StartIcon="@Icons.Material.Filled.Settings" |
|||
OnClick="@OnManageHostFeaturesClicked"> |
|||
@L["ManageHostFeatures"] |
|||
</MudButton> |
|||
</MudForm> |
|||
|
|||
<FeatureManagementModal @ref="FeatureManagementModal"/> |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.AspNetCore.Components; |
|||
using Volo.Abp.AspNetCore.Components; |
|||
using Volo.Abp.Authorization.Permissions; |
|||
using Volo.Abp.FeatureManagement.Blazor.MudBlazor.Components; |
|||
using Volo.Abp.FeatureManagement.Localization; |
|||
using Volo.Abp.Features; |
|||
|
|||
namespace Volo.Abp.FeatureManagement.Blazor.MudBlazor.Components.FeatureSettingGroup; |
|||
|
|||
public partial class FeatureSettingManagementComponent : AbpComponentBase |
|||
{ |
|||
[Inject] |
|||
protected PermissionChecker PermissionChecker { get; set; } = default!; |
|||
|
|||
protected FeatureManagementModal? FeatureManagementModal; |
|||
|
|||
protected FeatureSettingViewModel? Settings; |
|||
|
|||
public FeatureSettingManagementComponent() |
|||
{ |
|||
LocalizationResource = typeof(AbpFeatureManagementResource); |
|||
} |
|||
|
|||
protected async override Task OnInitializedAsync() |
|||
{ |
|||
Settings = new FeatureSettingViewModel |
|||
{ |
|||
HasManageHostFeaturesPermission = await AuthorizationService.IsGrantedAsync(FeatureManagementPermissions.ManageHostFeatures) |
|||
}; |
|||
} |
|||
|
|||
protected virtual async Task OnManageHostFeaturesClicked() |
|||
{ |
|||
if (FeatureManagementModal != null) |
|||
{ |
|||
await FeatureManagementModal.OpenAsync(TenantFeatureValueProvider.ProviderName); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Volo.Abp.FeatureManagement.Blazor.MudBlazor.Components.FeatureSettingGroup; |
|||
|
|||
public class FeatureSettingViewModel |
|||
{ |
|||
public bool HasManageHostFeaturesPermission { get; set; } |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Localization; |
|||
using Volo.Abp.FeatureManagement.Blazor.MudBlazor.Components.FeatureSettingGroup; |
|||
using Volo.Abp.FeatureManagement.Localization; |
|||
using Volo.Abp.SettingManagement.Blazor.MudBlazor; |
|||
|
|||
namespace Volo.Abp.FeatureManagement.Blazor.MudBlazor.Settings; |
|||
|
|||
public class FeatureSettingManagementComponentContributor: ISettingComponentContributor |
|||
{ |
|||
public virtual async Task ConfigureAsync(SettingComponentCreationContext context) |
|||
{ |
|||
if (!await CheckPermissionsInternalAsync(context)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var l = context.ServiceProvider.GetRequiredService<IStringLocalizer<AbpFeatureManagementResource>>(); |
|||
context.Groups.Add( |
|||
new SettingComponentGroup( |
|||
"Volo.Abp.Feature", |
|||
l["Permission:FeatureManagement"], |
|||
typeof(FeatureSettingManagementComponent) |
|||
) |
|||
); |
|||
} |
|||
|
|||
public virtual async Task<bool> CheckPermissionsAsync(SettingComponentCreationContext context) |
|||
{ |
|||
return await CheckPermissionsInternalAsync(context); |
|||
} |
|||
|
|||
protected virtual async Task<bool> CheckPermissionsInternalAsync(SettingComponentCreationContext context) |
|||
{ |
|||
var authorizationService = context.ServiceProvider.GetRequiredService<IAuthorizationService>(); |
|||
|
|||
return await authorizationService.IsGrantedAsync(FeatureManagementPermissions.ManageHostFeatures); |
|||
} |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
@using Microsoft.AspNetCore.Components.Web |
|||
@using Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor |
|||
@using MudBlazor |
|||
@using Volo.Abp.MudBlazorUI |
|||
Loading…
Reference in new issue