Browse Source

Merge pull request #5361 from abpframework/liangshiwei/feature-management

Feature Management Modal improvements
pull/5416/head
Halil İbrahim Kalkan 6 years ago
committed by GitHub
parent
commit
1f06e06ecd
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainSharedModule.cs
  2. 6
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureManagementDomainErrorCodes.cs
  3. 14
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureValueInvalidException.cs
  4. 5
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/ar.json
  5. 5
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/de.json
  6. 3
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/en.json
  7. 3
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/ru.json
  8. 3
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/tr.json
  9. 3
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/zh-Hans.json
  10. 3
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/zh-Hant.json
  11. 17
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs
  12. 83
      modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml
  13. 75
      modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/feature-management-modal.js

6
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainSharedModule.cs

@ -1,5 +1,6 @@
using Volo.Abp.FeatureManagement.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
using Volo.Abp.Validation;
using Volo.Abp.Validation.Localization;
@ -27,6 +28,11 @@ namespace Volo.Abp.FeatureManagement
typeof(AbpValidationResource)
).AddVirtualJson("Volo/Abp/FeatureManagement/Localization/Domain");
});
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace("Volo.Abp.FeatureManagement", typeof(AbpFeatureManagementResource));
});
}
}
}

6
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureManagementDomainErrorCodes.cs

@ -1,7 +1,9 @@
namespace Volo.Abp.FeatureManagement
using System;
namespace Volo.Abp.FeatureManagement
{
public static class FeatureManagementDomainErrorCodes
{
public const string FeatureValueInvalid = "Volo.Abp.FeatureManagement:InvalidFeatureValue";
}
}

14
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureValueInvalidException.cs

@ -0,0 +1,14 @@
using System;
namespace Volo.Abp.FeatureManagement
{
[Serializable]
public class FeatureValueInvalidException : BusinessException
{
public FeatureValueInvalidException(string name) :
base(FeatureManagementDomainErrorCodes.FeatureValueInvalid)
{
WithData("0", name);
}
}
}

5
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/ar.json

@ -2,6 +2,7 @@
"culture": "ar",
"texts": {
"Features": "المميزات",
"NoFeatureFoundMessage": "لا توجد أي ميزة متاحة."
"NoFeatureFoundMessage": "لا توجد أي ميزة متاحة.",
"Volo.Abp.FeatureManagement:InvalidFeatureValue" : "{0} قيمة الميزة غير صالحة!"
}
}
}

5
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/de.json

@ -2,6 +2,7 @@
"culture": "de",
"texts": {
"Features": "Funktionen",
"NoFeatureFoundMessage": "Es ist keine Funktion verfügbar."
"NoFeatureFoundMessage": "Es ist keine Funktion verfügbar.",
"Volo.Abp.FeatureManagement:InvalidFeatureValue" : "Der {0} -Feature-Wert ist ungültig!"
}
}
}

3
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/en.json

@ -4,6 +4,7 @@
"Features": "Features",
"NoFeatureFoundMessage": "There isn't any available feature.",
"Permission:FeatureManagement": "Feature management",
"Permission:FeatureManagement.ManageHostFeatures": "Manage Host features"
"Permission:FeatureManagement.ManageHostFeatures": "Manage Host features",
"Volo.Abp.FeatureManagement:InvalidFeatureValue" : "{0} feature value is not valid!"
}
}

3
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/ru.json

@ -2,6 +2,7 @@
"culture": "ru",
"texts": {
"Features": "Функциональные возможности",
"NoFeatureFoundMessage": "Нет доступных функциональных возможностей."
"NoFeatureFoundMessage": "Нет доступных функциональных возможностей.",
"Volo.Abp.FeatureManagement:InvalidFeatureValue" : "Недопустимое значение функции {0}!"
}
}

3
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/tr.json

@ -4,6 +4,7 @@
"Features": "Özellikler",
"NoFeatureFoundMessage": "Hiç özellik yok.",
"Permission:FeatureManagement": "Özellik yönetimi",
"Permission:FeatureManagement.ManageHostFeatures": "Host özelliklerini düzenle"
"Permission:FeatureManagement.ManageHostFeatures": "Host özelliklerini düzenle",
"Volo.Abp.FeatureManagement:InvalidFeatureValue" : "{0} özellik değeri geçerli değil!"
}
}

3
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/zh-Hans.json

@ -4,6 +4,7 @@
"Features": "功能",
"NoFeatureFoundMessage": "没有可用的功能.",
"Permission:FeatureManagement": "特性管理",
"Permission:FeatureManagement.ManageHostFeatures": "管理Host特性"
"Permission:FeatureManagement.ManageHostFeatures": "管理Host特性",
"Volo.Abp.FeatureManagement:InvalidFeatureValue" : "{0}功能的值无效!"
}
}

3
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/zh-Hant.json

@ -4,6 +4,7 @@
"Features": "功能",
"NoFeatureFoundMessage": "沒有可用的功能.",
"Permission:FeatureManagement": "功能管理",
"Permission:FeatureManagement.ManageHostFeatures": "管理Host功能"
"Permission:FeatureManagement.ManageHostFeatures": "管理Host功能",
"Volo.Abp.FeatureManagement:InvalidFeatureValue" : "{0}功能的值無效!"
}
}

17
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Features;
@ -14,15 +15,18 @@ namespace Volo.Abp.FeatureManagement
protected IFeatureDefinitionManager FeatureDefinitionManager { get; }
protected List<IFeatureManagementProvider> Providers => _lazyProviders.Value;
protected FeatureManagementOptions Options { get; }
protected IStringLocalizerFactory StringLocalizerFactory { get; }
private readonly Lazy<List<IFeatureManagementProvider>> _lazyProviders;
public FeatureManager(
IOptions<FeatureManagementOptions> options,
IServiceProvider serviceProvider,
IFeatureDefinitionManager featureDefinitionManager)
IFeatureDefinitionManager featureDefinitionManager,
IStringLocalizerFactory stringLocalizerFactory)
{
FeatureDefinitionManager = featureDefinitionManager;
StringLocalizerFactory = stringLocalizerFactory;
Options = options.Value;
//TODO: Instead, use IHybridServiceScopeFactory and create a scope..?
@ -57,7 +61,8 @@ namespace Volo.Abp.FeatureManagement
.Select(x => new FeatureNameValue(x.Name, x.Value)).ToList();
}
public async Task<FeatureNameValueWithGrantedProvider> GetOrNullWithProviderAsync(string name, string providerName, string providerKey, bool fallback = true)
public async Task<FeatureNameValueWithGrantedProvider> GetOrNullWithProviderAsync(string name,
string providerName, string providerKey, bool fallback = true)
{
Check.NotNull(name, nameof(name));
Check.NotNull(providerName, nameof(providerName));
@ -65,7 +70,8 @@ namespace Volo.Abp.FeatureManagement
return await GetOrNullInternalAsync(name, providerName, providerKey, fallback);
}
public async Task<List<FeatureNameValueWithGrantedProvider>> GetAllWithProviderAsync(string providerName, string providerKey, bool fallback = true)
public async Task<List<FeatureNameValueWithGrantedProvider>> GetAllWithProviderAsync(string providerName,
string providerKey, bool fallback = true)
{
Check.NotNull(providerName, nameof(providerName));
@ -128,6 +134,11 @@ namespace Volo.Abp.FeatureManagement
var feature = FeatureDefinitionManager.Get(name);
if (!feature.ValueType.Validator.IsValid(value))
{
throw new FeatureValueInvalidException(feature.DisplayName.Localize(StringLocalizerFactory));
}
var providers = Enumerable
.Reverse(Providers)
.SkipWhile(p => p.Name != providerName)

83
modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml

@ -24,55 +24,80 @@
<abp-modal-header title="@(L["Features"].Value)"></abp-modal-header>
@if (Model.FeatureListResultDto != null && Model.FeatureListResultDto.Groups.Any())
{
var featureGroups = Model.FeatureListResultDto.Groups;
<abp-modal-body class="ml-4">
<input asp-for="@Model.ProviderKey"/>
<input asp-for="@Model.ProviderName"/>
<abp-tabs name="FeaturesTabs" tab-style="PillVertical" vertical-header-size="_4" class="custom-scroll-container">
@for (var i = 0; i < Model.FeatureListResultDto.Groups.Count; i++)
@for (var i = 0; i < featureGroups.Count; i++)
{
<abp-tab title="@Model.FeatureListResultDto.Groups[i].DisplayName" name="v-pills-tab-@Model.FeatureListResultDto.Groups[i].GetNormalizedGroupName()">
<h4>@Model.FeatureListResultDto.Groups[i].DisplayName</h4>
<abp-tab title="@featureGroups[i].DisplayName" name="v-pills-tab-@featureGroups[i].GetNormalizedGroupName()">
<h4>@featureGroups[i].DisplayName</h4>
<hr class="mt-2 mb-3"/>
<div class="custom-scroll-content">
<div class="pl-1 pt-1">
@for (var j = 0; j < Model.FeatureListResultDto.Groups[i].Features.Count; j++)
@for (var j = 0; j < featureGroups[i].Features.Count; j++)
{
var feature = Model.FeatureListResultDto.Groups[i].Features[j];
var feature = featureGroups[i].Features[j];
var disabled = Model.IsDisabled(feature.Provider.Name);
<div class="mt-2" style="padding-left: @(feature.Depth * 20)px">
<span class="mr-2">@feature.DisplayName @(disabled ? $"({feature.Provider.Name})" : "")</span>
<div class="mt-2">
<input type="text" name="FeatureGroups[@i].Features[@j].ProviderName" value="@feature.Provider.Name" hidden/>
<input type="text" name="FeatureGroups[@i].Features[@j].Type" value="@feature.ValueType?.Name" hidden/>
@if (feature.ValueType is ToggleStringValueType)
{
<abp-input asp-for="@featureGroups[i].Features[j].Value"
type="checkbox"
name="FeatureGroups[@i].Features[@j].BoolValue"
label="@feature.DisplayName"
disabled="@disabled"
group-data-feature-name="@feature.Name"
group-data-parent-name="@(feature.ParentName ?? "")"
group-style="margin-left: @(feature.Depth * 20)px"/>
}
@if (feature.ValueType is FreeTextStringValueType)
{
<input type="text" name="FeatureGroups[@i].Features[@j].Name" value="@feature.Name" hidden/>
<input disabled="@disabled" type="text" name="FeatureGroups[@i].Features[@j].Value" value="@feature.Value"/>
var type = "text";
if(feature.ValueType.Validator is NumericValueValidator)
{
type = "number";
}
<abp-input asp-for="@featureGroups[i].Features[j].Value"
label="@feature.DisplayName"
disabled="@disabled"
type="@type"
group-data-feature-name="@feature.Name"
group-data-parent-name="@(feature.ParentName ?? "")"
group-style="margin-left: @(feature.Depth * 25)px"/>
}
@if (feature.ValueType is SelectionStringValueType)
@if (feature.ValueType is SelectionStringValueType selectType)
{
<input type="text" name="FeatureGroups[@i].Features[@j].Name" value="@feature.Name" hidden/>
<select disabled="@disabled" name="FeatureGroups[@i].Features[@j].Value">
@foreach (var item in (feature.ValueType as SelectionStringValueType).ItemSource.Items)
{
if (item.Value == feature.Value)
{
<option value="@item.Value" selected="selected"> @CreateHtmlLocalizer(item.DisplayText.ResourceName).GetString(item.DisplayText.Name) </option>
}
else
<div data-feature-name="@feature.Name" data-parent-name="@(feature.ParentName ?? "")" style="margin-left: @(feature.Depth * 25)px" class="form-group">
<label for="@feature.Name">@feature.DisplayName</label>
<select id="@feature.Name" name="FeatureGroups[@i].Features[@j].Value" class="custom-select form-control">
@foreach (var item in selectType.ItemSource.Items)
{
<option value="@item.Value"> @CreateHtmlLocalizer(item.DisplayText.ResourceName).GetString(item.DisplayText.Name) </option>
if (item.Value == feature.Value)
{
<option value="@item.Value" selected="selected"> @CreateHtmlLocalizer(item.DisplayText.ResourceName).GetString(item.DisplayText.Name) </option>
}
else
{
<option value="@item.Value"> @CreateHtmlLocalizer(item.DisplayText.ResourceName).GetString(item.DisplayText.Name) </option>
}
}
}
</select>
}
@if (feature.ValueType is ToggleStringValueType)
{
<input type="text" name="FeatureGroups[@i].Features[@j].Name" value="@feature.Name" hidden/>
<input disabled="@disabled" type="checkbox" class="FeatureValueCheckbox" name="FeatureGroups[@i].Features[@j].BoolValue" value="@feature.Value"
@Html.Raw(feature.Value == "True" ? "checked" : "")/>
</select>
</div>
}
<input value="@feature.Name" name="FeatureGroups[@i].Features[@j].Name" hidden=""/>
</div>
}
</div>

75
modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/feature-management-modal.js

@ -3,12 +3,75 @@
abp.modals = abp.modals || {};
abp.modals.FeatureManagement = function () {
$('.FeatureValueCheckbox').change(function () {
if (this.checked) {
$(this).val('true');
} else {
$(this).val('false');
function checkParents($tab, $element, className) {
var parentName = $element
.closest(className)
.attr('data-parent-name');
if (!parentName) {
return;
}
$tab.find('.custom-checkbox')
.filter('[data-feature-name="' + parentName + '"]')
.find('input[type="checkbox"]')
.each(function () {
var $parent = $(this);
$parent.prop('checked', true);
checkParents($tab, $parent, className);
});
}
function uncheckChildren($tab, $checkBox) {
var featureName = $checkBox
.closest('.custom-checkbox')
.attr('data-feature-name');
if (!featureName) {
return;
}
});
$tab.find('.custom-checkbox')
.filter('[data-parent-name="' + featureName + '"]')
.find('input[type="checkbox"]')
.each(function () {
var $child = $(this);
$child.prop('checked', false);
uncheckChildren($tab, $child);
});
}
this.initDom = function ($el) {
$el.find('.tab-pane').each(function () {
var $tab = $(this);
$tab.find('input[type="checkbox"]')
.each(function () {
var $checkBox = $(this);
$checkBox.change(function () {
if ($checkBox.is(':checked')) {
checkParents($tab, $checkBox, '.custom-checkbox')
} else {
uncheckChildren($tab, $checkBox);
}
});
});
$tab.find('.form-control')
.each(function () {
var $element = $(this);
$element.change(function () {
checkParents($tab, $element, '.form-group')
});
});
});
$(function () {
$('.custom-scroll-content').mCustomScrollbar({
theme: 'minimal-dark',
});
$('.custom-scroll-container > .col-4').mCustomScrollbar({
theme: 'minimal-dark',
});
});
};
};
})(jQuery);

Loading…
Cancel
Save