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