mirror of https://github.com/abpframework/abp.git
442 changed files with 8004 additions and 1876 deletions
@ -0,0 +1,20 @@ |
|||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using Volo.Utils.SolutionTemplating.Files; |
||||
|
|
||||
|
namespace Volo.Utils.SolutionTemplating.Building |
||||
|
{ |
||||
|
public static class ProjectBuildContextExtensions |
||||
|
{ |
||||
|
public static FileEntry GetFile(this ProjectBuildContext context, string filePath) |
||||
|
{ |
||||
|
var file = context.Files.FirstOrDefault(f => f.Name == filePath); |
||||
|
if (file == null) |
||||
|
{ |
||||
|
throw new ApplicationException("Could not find file: " + filePath); |
||||
|
} |
||||
|
|
||||
|
return file; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,22 +1,7 @@ |
|||||
using System; |
namespace Volo.Utils.SolutionTemplating.Building |
||||
using System.Linq; |
|
||||
using Volo.Utils.SolutionTemplating.Files; |
|
||||
|
|
||||
namespace Volo.Utils.SolutionTemplating.Building |
|
||||
{ |
{ |
||||
public abstract class ProjectBuildPipelineStep |
public abstract class ProjectBuildPipelineStep |
||||
{ |
{ |
||||
public abstract void Execute(ProjectBuildContext context); |
public abstract void Execute(ProjectBuildContext context); |
||||
|
|
||||
protected FileEntry GetFile(ProjectBuildContext context, string filePath) |
|
||||
{ |
|
||||
var file = context.Files.FirstOrDefault(f => f.Name == filePath); |
|
||||
if (file == null) |
|
||||
{ |
|
||||
throw new ApplicationException("Could not find file: " + filePath); |
|
||||
} |
|
||||
|
|
||||
return file; |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
File diff suppressed because it is too large
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
@ -0,0 +1,24 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Features; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Client |
||||
|
{ |
||||
|
public class RemoteFeatureChecker : FeatureCheckerBase |
||||
|
{ |
||||
|
protected ICachedApplicationConfigurationClient ConfigurationClient { get; } |
||||
|
|
||||
|
public RemoteFeatureChecker(ICachedApplicationConfigurationClient configurationClient) |
||||
|
{ |
||||
|
ConfigurationClient = configurationClient; |
||||
|
} |
||||
|
|
||||
|
public override async Task<string> GetOrNullAsync(string name) |
||||
|
{ |
||||
|
var configuration = await ConfigurationClient.GetAsync(); |
||||
|
return configuration.Features.Values.GetOrDefault(name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations |
||||
|
{ |
||||
|
[Serializable] |
||||
|
public class ApplicationFeatureConfigurationDto |
||||
|
{ |
||||
|
public Dictionary<string, string> Values { get; set; } |
||||
|
|
||||
|
public ApplicationFeatureConfigurationDto() |
||||
|
{ |
||||
|
Values = new Dictionary<string, string>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using System.IO; |
||||
|
|
||||
|
namespace Microsoft.AspNetCore.Http |
||||
|
{ |
||||
|
public static class AbpFormFileExtensions |
||||
|
{ |
||||
|
public static byte[] GetAllBytes(this IFormFile file) |
||||
|
{ |
||||
|
using (var stream = file.OpenReadStream()) |
||||
|
{ |
||||
|
return stream.GetAllBytes(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc.Abstractions; |
||||
|
using Volo.Abp.Aspects; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Features; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Features |
||||
|
{ |
||||
|
public class AbpFeatureActionFilter : IAsyncActionFilter, ITransientDependency |
||||
|
{ |
||||
|
private readonly IMethodInvocationFeatureCheckerService _methodInvocationAuthorizationService; |
||||
|
|
||||
|
public AbpFeatureActionFilter(IMethodInvocationFeatureCheckerService methodInvocationAuthorizationService) |
||||
|
{ |
||||
|
_methodInvocationAuthorizationService = methodInvocationAuthorizationService; |
||||
|
} |
||||
|
|
||||
|
public async Task OnActionExecutionAsync( |
||||
|
ActionExecutingContext context, |
||||
|
ActionExecutionDelegate next) |
||||
|
{ |
||||
|
if (!context.ActionDescriptor.IsControllerAction()) |
||||
|
{ |
||||
|
await next(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var methodInfo = context.ActionDescriptor.GetMethodInfo(); |
||||
|
|
||||
|
using (AbpCrossCuttingConcerns.Applying(context.Controller, AbpCrossCuttingConcerns.FeatureChecking)) |
||||
|
{ |
||||
|
await _methodInvocationAuthorizationService.CheckAsync( |
||||
|
new MethodInvocationFeatureCheckerContext(methodInfo) |
||||
|
); |
||||
|
|
||||
|
await next(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,7 +0,0 @@ |
|||||
namespace Volo.Abp.Authorization |
|
||||
{ |
|
||||
public interface IAuthorizationEnabled |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
@ -1,8 +1,6 @@ |
|||||
using Volo.Abp.DependencyInjection; |
namespace Volo.Abp.Authorization.Permissions |
||||
|
|
||||
namespace Volo.Abp.Authorization.Permissions |
|
||||
{ |
{ |
||||
public interface IPermissionDefinitionProvider : ISingletonDependency |
public interface IPermissionDefinitionProvider |
||||
{ |
{ |
||||
void Define(IPermissionDefinitionContext context); |
void Define(IPermissionDefinitionContext context); |
||||
} |
} |
||||
|
|||||
@ -1,6 +1,8 @@ |
|||||
namespace Volo.Abp.Authorization.Permissions |
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Authorization.Permissions |
||||
{ |
{ |
||||
public abstract class PermissionDefinitionProvider : IPermissionDefinitionProvider |
public abstract class PermissionDefinitionProvider : IPermissionDefinitionProvider, ITransientDependency |
||||
{ |
{ |
||||
public abstract void Define(IPermissionDefinitionContext context); |
public abstract void Define(IPermissionDefinitionContext context); |
||||
} |
} |
||||
|
|||||
@ -1,18 +1,52 @@ |
|||||
using Volo.Abp.Localization; |
using Microsoft.Extensions.DependencyInjection; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Localization; |
||||
using Volo.Abp.Modularity; |
using Volo.Abp.Modularity; |
||||
using Volo.Abp.MultiTenancy; |
using Volo.Abp.MultiTenancy; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
namespace Volo.Abp.Features |
namespace Volo.Abp.Features |
||||
{ |
{ |
||||
[DependsOn( |
[DependsOn( |
||||
typeof(AbpLocalizationAbstractionsModule), |
typeof(AbpLocalizationAbstractionsModule), |
||||
typeof(AbpMultiTenancyAbstractionsModule) |
typeof(AbpMultiTenancyAbstractionsModule), |
||||
|
typeof(AbpValidationModule) |
||||
)] |
)] |
||||
public class AbpFeaturesModule : AbpModule |
public class AbpFeaturesModule : AbpModule |
||||
{ |
{ |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.OnRegistred(FeatureInterceptorRegistrar.RegisterIfNeeded); |
||||
|
AutoAddDefinitionProviders(context.Services); |
||||
|
} |
||||
|
|
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
{ |
{ |
||||
|
context.Services.Configure<FeatureOptions>(options => |
||||
|
{ |
||||
|
options.ValueProviders.Add<DefaultValueFeatureValueProvider>(); |
||||
|
options.ValueProviders.Add<EditionFeatureValueProvider>(); |
||||
|
options.ValueProviders.Add<TenantFeatureValueProvider>(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private static void AutoAddDefinitionProviders(IServiceCollection services) |
||||
|
{ |
||||
|
var definitionProviders = new List<Type>(); |
||||
|
|
||||
|
services.OnRegistred(context => |
||||
|
{ |
||||
|
if (typeof(IFeatureDefinitionProvider).IsAssignableFrom(context.ImplementationType)) |
||||
|
{ |
||||
|
definitionProviders.Add(context.ImplementationType); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
services.Configure<FeatureOptions>(options => |
||||
|
{ |
||||
|
options.DefinitionProviders.AddIfNotContains(definitionProviders); |
||||
|
}); |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,22 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class DefaultValueFeatureValueProvider : FeatureValueProvider //TODO: Directly implement IFeatureValueProvider
|
||||
|
{ |
||||
|
public const string ProviderName = "Default"; |
||||
|
|
||||
|
public override string Name => ProviderName; |
||||
|
|
||||
|
public DefaultValueFeatureValueProvider(IFeatureStore settingStore) |
||||
|
: base(settingStore) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public override Task<string> GetOrNullAsync(FeatureDefinition setting) |
||||
|
{ |
||||
|
return Task.FromResult(setting.DefaultValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
[AttributeUsage(AttributeTargets.Method)] |
||||
|
public class DisableFeatureCheckAttribute : Attribute |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
using System.Security.Principal; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Security.Claims; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class EditionFeatureValueProvider : FeatureValueProvider |
||||
|
{ |
||||
|
public const string ProviderName = "Edition"; |
||||
|
|
||||
|
public override string Name => ProviderName; |
||||
|
|
||||
|
protected ICurrentPrincipalAccessor PrincipalAccessor; |
||||
|
|
||||
|
public EditionFeatureValueProvider(IFeatureStore featureStore, ICurrentPrincipalAccessor principalAccessor) |
||||
|
: base(featureStore) |
||||
|
{ |
||||
|
PrincipalAccessor = principalAccessor; |
||||
|
} |
||||
|
|
||||
|
public override async Task<string> GetOrNullAsync(FeatureDefinition feature) |
||||
|
{ |
||||
|
var editionId = PrincipalAccessor.Principal?.FindEditionId(); |
||||
|
if (editionId == null) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return await FeatureStore.GetOrNullAsync(feature.Name, Name, editionId.Value.ToString("N")); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public abstract class FeatureCheckerBase : IFeatureChecker, ITransientDependency |
||||
|
{ |
||||
|
public abstract Task<string> GetOrNullAsync(string name); |
||||
|
|
||||
|
public virtual async Task<bool> IsEnabledAsync(string name) |
||||
|
{ |
||||
|
var value = await GetOrNullAsync(name); |
||||
|
if (value == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
return bool.Parse(value); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
throw new AbpException( |
||||
|
$"The value '{value}' for the feature '{name}' should be a boolean, but was not!", |
||||
|
ex |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,38 +1,51 @@ |
|||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
using System.Collections.Immutable; |
using Volo.Abp.Localization; |
||||
|
|
||||
namespace Volo.Abp.Features |
namespace Volo.Abp.Features |
||||
{ |
{ |
||||
public class FeatureDefinitionContext : IFeatureDefinitionContext |
public class FeatureDefinitionContext : IFeatureDefinitionContext |
||||
{ |
{ |
||||
protected Dictionary<string, FeatureDefinition> Features { get; } |
internal Dictionary<string, FeatureGroupDefinition> Groups { get; } |
||||
|
|
||||
public FeatureDefinitionContext(Dictionary<string, FeatureDefinition> features) |
public FeatureDefinitionContext() |
||||
{ |
{ |
||||
Features = features; |
Groups = new Dictionary<string, FeatureGroupDefinition>(); |
||||
} |
} |
||||
|
|
||||
public virtual FeatureDefinition GetOrNull(string name) |
public FeatureGroupDefinition AddGroup(string name, ILocalizableString displayName = null) |
||||
{ |
{ |
||||
return Features.GetOrDefault(name); |
Check.NotNull(name, nameof(name)); |
||||
} |
|
||||
|
|
||||
public virtual IReadOnlyList<FeatureDefinition> GetAll() |
if (Groups.ContainsKey(name)) |
||||
{ |
{ |
||||
return Features.Values.ToImmutableList(); |
throw new AbpException($"There is already an existing permission group with name: {name}"); |
||||
|
} |
||||
|
|
||||
|
return Groups[name] = new FeatureGroupDefinition(name, displayName); |
||||
} |
} |
||||
|
|
||||
public virtual void Add(params FeatureDefinition[] definitions) |
public FeatureGroupDefinition GetGroupOrNull(string name) |
||||
{ |
{ |
||||
if (definitions.IsNullOrEmpty()) |
Check.NotNull(name, nameof(name)); |
||||
|
|
||||
|
if (!Groups.ContainsKey(name)) |
||||
{ |
{ |
||||
return; |
return null; |
||||
} |
} |
||||
|
|
||||
foreach (var definition in definitions) |
return Groups[name]; |
||||
|
} |
||||
|
|
||||
|
public void RemoveGroup(string name) |
||||
|
{ |
||||
|
Check.NotNull(name, nameof(name)); |
||||
|
|
||||
|
if (!Groups.ContainsKey(name)) |
||||
{ |
{ |
||||
Features[definition.Name] = definition; |
throw new AbpException($"Undefined feature group: '{name}'."); |
||||
} |
} |
||||
|
|
||||
|
Groups.Remove(name); |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,111 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Validation.StringValues; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class FeatureGroupDefinition |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Unique name of the group.
|
||||
|
/// </summary>
|
||||
|
public string Name { get; } |
||||
|
|
||||
|
public Dictionary<string, object> Properties { get; } |
||||
|
|
||||
|
public ILocalizableString DisplayName |
||||
|
{ |
||||
|
get => _displayName; |
||||
|
set => _displayName = Check.NotNull(value, nameof(value)); |
||||
|
} |
||||
|
private ILocalizableString _displayName; |
||||
|
|
||||
|
public IReadOnlyList<FeatureDefinition> Features => _features.ToImmutableList(); |
||||
|
private readonly List<FeatureDefinition> _features; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets/sets a key-value on the <see cref="Properties"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">Name of the property</param>
|
||||
|
/// <returns>
|
||||
|
/// Returns the value in the <see cref="Properties"/> dictionary by given <see cref="name"/>.
|
||||
|
/// Returns null if given <see cref="name"/> is not present in the <see cref="Properties"/> dictionary.
|
||||
|
/// </returns>
|
||||
|
public object this[string name] |
||||
|
{ |
||||
|
get => Properties.GetOrDefault(name); |
||||
|
set => Properties[name] = value; |
||||
|
} |
||||
|
|
||||
|
protected internal FeatureGroupDefinition( |
||||
|
string name, |
||||
|
ILocalizableString displayName = null) |
||||
|
{ |
||||
|
Name = name; |
||||
|
DisplayName = displayName ?? new FixedLocalizableString(Name); |
||||
|
|
||||
|
Properties = new Dictionary<string, object>(); |
||||
|
_features = new List<FeatureDefinition>(); |
||||
|
} |
||||
|
|
||||
|
public virtual FeatureDefinition AddFeature( |
||||
|
string name, |
||||
|
string defaultValue = null, |
||||
|
ILocalizableString displayName = null, |
||||
|
ILocalizableString description = null, |
||||
|
IStringValueType valueType = null, |
||||
|
bool isVisibleToClients = true) |
||||
|
{ |
||||
|
var feature = new FeatureDefinition( |
||||
|
name, |
||||
|
defaultValue, |
||||
|
displayName, |
||||
|
description, |
||||
|
valueType, |
||||
|
isVisibleToClients |
||||
|
); |
||||
|
|
||||
|
_features.Add(feature); |
||||
|
|
||||
|
return feature; |
||||
|
} |
||||
|
|
||||
|
public virtual List<FeatureDefinition> GetFeaturesWithChildren() |
||||
|
{ |
||||
|
var features = new List<FeatureDefinition>(); |
||||
|
|
||||
|
foreach (var feature in _features) |
||||
|
{ |
||||
|
AddFeatureToListRecursively(features, feature); |
||||
|
} |
||||
|
|
||||
|
return features; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets a property in the <see cref="Properties"/> dictionary.
|
||||
|
/// This is a shortcut for nested calls on this object.
|
||||
|
/// </summary>
|
||||
|
public virtual FeatureGroupDefinition WithProperty(string key, object value) |
||||
|
{ |
||||
|
Properties[key] = value; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
private void AddFeatureToListRecursively(List<FeatureDefinition> features, FeatureDefinition feature) |
||||
|
{ |
||||
|
features.Add(feature); |
||||
|
|
||||
|
foreach (var child in feature.Children) |
||||
|
{ |
||||
|
AddFeatureToListRecursively(features, child); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return $"[{nameof(FeatureGroupDefinition)} {Name}]"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Aspects; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.DynamicProxy; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class FeatureInterceptor : AbpInterceptor, ITransientDependency |
||||
|
{ |
||||
|
private readonly IMethodInvocationFeatureCheckerService _methodInvocationFeatureCheckerService; |
||||
|
|
||||
|
public FeatureInterceptor( |
||||
|
IMethodInvocationFeatureCheckerService methodInvocationFeatureCheckerService) |
||||
|
{ |
||||
|
_methodInvocationFeatureCheckerService = methodInvocationFeatureCheckerService; |
||||
|
} |
||||
|
|
||||
|
public override void Intercept(IAbpMethodInvocation invocation) |
||||
|
{ |
||||
|
if (AbpCrossCuttingConcerns.IsApplied( |
||||
|
invocation.TargetObject, |
||||
|
AbpCrossCuttingConcerns.FeatureChecking)) |
||||
|
{ |
||||
|
invocation.Proceed(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
AsyncHelper.RunSync(() => CheckFeaturesAsync(invocation)); |
||||
|
invocation.Proceed(); |
||||
|
} |
||||
|
|
||||
|
public override async Task InterceptAsync(IAbpMethodInvocation invocation) |
||||
|
{ |
||||
|
if (AbpCrossCuttingConcerns.IsApplied( |
||||
|
invocation.TargetObject, |
||||
|
AbpCrossCuttingConcerns.FeatureChecking)) |
||||
|
{ |
||||
|
await invocation.ProceedAsync(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
AsyncHelper.RunSync(() => CheckFeaturesAsync(invocation)); |
||||
|
await invocation.ProceedAsync(); |
||||
|
} |
||||
|
|
||||
|
protected virtual Task CheckFeaturesAsync(IAbpMethodInvocation invocation) |
||||
|
{ |
||||
|
return _methodInvocationFeatureCheckerService.CheckAsync( |
||||
|
new MethodInvocationFeatureCheckerContext( |
||||
|
invocation.Method |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public static class FeatureInterceptorRegistrar |
||||
|
{ |
||||
|
public static void RegisterIfNeeded(IOnServiceRegistredContext context) |
||||
|
{ |
||||
|
if (ShouldIntercept(context.ImplementationType)) |
||||
|
{ |
||||
|
context.Interceptors.TryAdd<FeatureInterceptor>(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static bool ShouldIntercept(Type type) |
||||
|
{ |
||||
|
return type.IsDefined(typeof(RequiresFeatureAttribute), true) || |
||||
|
AnyMethodHasRequiresFeatureAttribute(type); |
||||
|
} |
||||
|
|
||||
|
private static bool AnyMethodHasRequiresFeatureAttribute(Type implementationType) |
||||
|
{ |
||||
|
return implementationType |
||||
|
.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) |
||||
|
.Any(HasRequiresFeatureAttribute); |
||||
|
} |
||||
|
|
||||
|
private static bool HasRequiresFeatureAttribute(MemberInfo methodInfo) |
||||
|
{ |
||||
|
return methodInfo.IsDefined(typeof(RequiresFeatureAttribute), true); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,9 +1,14 @@ |
|||||
namespace Volo.Abp.Features |
using JetBrains.Annotations; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
{ |
{ |
||||
public interface IFeatureDefinitionContext |
public interface IFeatureDefinitionContext |
||||
{ |
{ |
||||
FeatureDefinition GetOrNull(string name); |
FeatureGroupDefinition AddGroup([NotNull] string name, ILocalizableString displayName = null); |
||||
|
|
||||
void Add(params FeatureDefinition[] definitions); |
FeatureGroupDefinition GetGroupOrNull(string name); |
||||
|
|
||||
|
void RemoveGroup(string name); |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public interface IMethodInvocationFeatureCheckerService |
||||
|
{ |
||||
|
Task CheckAsync( |
||||
|
MethodInvocationFeatureCheckerContext context |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System.Reflection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class MethodInvocationFeatureCheckerContext |
||||
|
{ |
||||
|
public MethodInfo Method { get; } |
||||
|
|
||||
|
public MethodInvocationFeatureCheckerContext(MethodInfo method) |
||||
|
{ |
||||
|
Method = method; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class MethodInvocationFeatureCheckerService : IMethodInvocationFeatureCheckerService, ITransientDependency |
||||
|
{ |
||||
|
private readonly IFeatureChecker _featureChecker; |
||||
|
|
||||
|
public MethodInvocationFeatureCheckerService( |
||||
|
IFeatureChecker featureChecker) |
||||
|
{ |
||||
|
_featureChecker = featureChecker; |
||||
|
} |
||||
|
|
||||
|
public async Task CheckAsync(MethodInvocationFeatureCheckerContext context) |
||||
|
{ |
||||
|
if (IsFeatureCheckDisabled(context)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
foreach (var requiresFeatureAttribute in GetRequiredFeatureAttributes(context.Method)) |
||||
|
{ |
||||
|
await _featureChecker.CheckEnabledAsync(requiresFeatureAttribute.RequiresAll, requiresFeatureAttribute.Features); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual bool IsFeatureCheckDisabled(MethodInvocationFeatureCheckerContext context) |
||||
|
{ |
||||
|
return context.Method |
||||
|
.GetCustomAttributes(true) |
||||
|
.OfType<DisableFeatureCheckAttribute>() |
||||
|
.Any(); |
||||
|
} |
||||
|
|
||||
|
protected virtual IEnumerable<RequiresFeatureAttribute> GetRequiredFeatureAttributes(MethodInfo methodInfo) |
||||
|
{ |
||||
|
var attributes = methodInfo |
||||
|
.GetCustomAttributes(true) |
||||
|
.OfType<RequiresFeatureAttribute>(); |
||||
|
|
||||
|
if (methodInfo.IsPublic) |
||||
|
{ |
||||
|
attributes = attributes |
||||
|
.Union( |
||||
|
methodInfo.DeclaringType |
||||
|
.GetCustomAttributes(true) |
||||
|
.OfType<RequiresFeatureAttribute>() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return attributes; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This attribute can be used on a class/method to declare that given class/method is available
|
||||
|
/// only if required feature(s) are enabled.
|
||||
|
/// </summary>
|
||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] |
||||
|
public class RequiresFeatureAttribute : Attribute |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A list of features to be checked if they are enabled.
|
||||
|
/// </summary>
|
||||
|
public string[] Features { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// If this property is set to true, all of the <see cref="Features"/> must be enabled.
|
||||
|
/// If it's false, at least one of the <see cref="Features"/> must be enabled.
|
||||
|
/// Default: false.
|
||||
|
/// </summary>
|
||||
|
public bool RequiresAll { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new instance of <see cref="RequiresFeatureAttribute"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="features">A list of features to be checked if they are enabled</param>
|
||||
|
public RequiresFeatureAttribute(params string[] features) |
||||
|
{ |
||||
|
Features = features ?? Array.Empty<string>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Reflection; |
||||
|
using JetBrains.Annotations; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using Volo.Abp.DynamicProxy; |
||||
|
using Volo.Abp.Reflection; |
||||
|
|
||||
|
namespace Volo.Abp.Localization |
||||
|
{ |
||||
|
public static class AbpStringLocalizerExtensions |
||||
|
{ |
||||
|
[NotNull] |
||||
|
public static IStringLocalizer GetInternalLocalizer( |
||||
|
[NotNull] this IStringLocalizer stringLocalizer) |
||||
|
{ |
||||
|
Check.NotNull(stringLocalizer, nameof(stringLocalizer)); |
||||
|
|
||||
|
var localizerType = stringLocalizer.GetType(); |
||||
|
if (!ReflectionHelper.IsAssignableToGenericType(localizerType, typeof(StringLocalizer<>))) |
||||
|
{ |
||||
|
return stringLocalizer; |
||||
|
} |
||||
|
|
||||
|
var localizerField = localizerType |
||||
|
.GetField( |
||||
|
"_localizer", |
||||
|
BindingFlags.Instance | |
||||
|
BindingFlags.NonPublic |
||||
|
); |
||||
|
|
||||
|
if (localizerField == null) |
||||
|
{ |
||||
|
throw new AbpException($"Could not find the _localizer field inside the {typeof(StringLocalizer<>).FullName} class. Probably its name has changed. Please report this issue to the ABP framework."); |
||||
|
} |
||||
|
|
||||
|
return localizerField.GetValue(stringLocalizer) as IStringLocalizer; |
||||
|
} |
||||
|
|
||||
|
public static IEnumerable<LocalizedString> GetAllStrings( |
||||
|
this IStringLocalizer stringLocalizer, |
||||
|
bool includeParentCultures, |
||||
|
bool includeBaseLocalizers) |
||||
|
{ |
||||
|
var internalLocalizer = (ProxyHelper.UnProxy(stringLocalizer) as IStringLocalizer).GetInternalLocalizer(); |
||||
|
if (internalLocalizer is IStringLocalizerSupportsInheritance stringLocalizerSupportsInheritance) |
||||
|
{ |
||||
|
return stringLocalizerSupportsInheritance.GetAllStrings( |
||||
|
includeParentCultures, |
||||
|
includeBaseLocalizers |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return stringLocalizer.GetAllStrings( |
||||
|
includeParentCultures |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
|
||||
|
namespace Volo.Abp.Localization |
||||
|
{ |
||||
|
public interface IStringLocalizerSupportsInheritance |
||||
|
{ |
||||
|
IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures, bool includeBaseLocalizers); |
||||
|
} |
||||
|
} |
||||
@ -1,10 +1,32 @@ |
|||||
using Volo.Abp.Modularity; |
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.Ui.Navigation; |
||||
|
using Volo.Abp.Ui.Navigation.Localization.Resource; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
namespace Volo.Abp.UI.Navigation |
namespace Volo.Abp.UI.Navigation |
||||
{ |
{ |
||||
[DependsOn(typeof(AbpUiModule))] |
[DependsOn(typeof(AbpUiModule))] |
||||
public class AbpUiNavigationModule : AbpModule |
public class AbpUiNavigationModule : AbpModule |
||||
{ |
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<VirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<AbpUiNavigationModule>(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Add<AbpUiNavigationResource>("en") |
||||
|
.AddVirtualJson("/Volo/Abp/Ui/Navigation/Localization/Resource"); |
||||
|
}); |
||||
|
|
||||
|
Configure<NavigationOptions>(options => |
||||
|
{ |
||||
|
options.MenuContributors.Add(new DefaultMenuContributor()); |
||||
|
}); |
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,59 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp.Ui.Navigation; |
||||
|
|
||||
|
namespace Volo.Abp.UI.Navigation |
||||
|
{ |
||||
|
public static class ApplicationMenuExtensions |
||||
|
{ |
||||
|
[NotNull] |
||||
|
public static ApplicationMenuItem GetAdministration( |
||||
|
[NotNull] this ApplicationMenu applicationMenu) |
||||
|
{ |
||||
|
return applicationMenu.GetMenuItem( |
||||
|
DefaultMenuNames.Application.Main.Administration |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
[NotNull] |
||||
|
public static ApplicationMenuItem GetMenuItem( |
||||
|
[NotNull] this IHasMenuItems menuWithItems, |
||||
|
string menuItemName) |
||||
|
{ |
||||
|
var menuItem = menuWithItems.GetMenuItemOrNull(menuItemName); |
||||
|
if (menuItem == null) |
||||
|
{ |
||||
|
throw new AbpException($"Could not find a menu item with given name: {menuItemName}"); |
||||
|
} |
||||
|
|
||||
|
return menuItem; |
||||
|
} |
||||
|
|
||||
|
[CanBeNull] |
||||
|
public static ApplicationMenuItem GetMenuItemOrNull( |
||||
|
[NotNull] this IHasMenuItems menuWithItems, |
||||
|
string menuItemName) |
||||
|
{ |
||||
|
Check.NotNull(menuWithItems, nameof(menuWithItems)); |
||||
|
|
||||
|
return menuWithItems.Items.FirstOrDefault(mi => mi.Name == menuItemName); |
||||
|
} |
||||
|
|
||||
|
[NotNull] |
||||
|
public static IHasMenuItems SetSubItemOrder( |
||||
|
[NotNull] this IHasMenuItems menuWithItems, |
||||
|
string menuItemName, |
||||
|
int order) |
||||
|
{ |
||||
|
Check.NotNull(menuWithItems, nameof(menuWithItems)); |
||||
|
|
||||
|
var menuItem = menuWithItems.GetMenuItemOrNull(menuItemName); |
||||
|
if (menuItem != null) |
||||
|
{ |
||||
|
menuItem.Order = order; |
||||
|
} |
||||
|
|
||||
|
return menuWithItems; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp.UI.Navigation; |
||||
|
|
||||
|
namespace Volo.Abp.Ui.Navigation |
||||
|
{ |
||||
|
public class ApplicationMenuItemList : List<ApplicationMenuItem> |
||||
|
{ |
||||
|
public ApplicationMenuItemList() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public ApplicationMenuItemList(int capacity) |
||||
|
: base(capacity) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public ApplicationMenuItemList(IEnumerable<ApplicationMenuItem> collection) |
||||
|
: base(collection) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public void Normalize() |
||||
|
{ |
||||
|
RemoveEmptyItems(); |
||||
|
Order(); |
||||
|
} |
||||
|
|
||||
|
private void RemoveEmptyItems() |
||||
|
{ |
||||
|
RemoveAll(item => item.IsLeaf && item.Url.IsNullOrEmpty()); |
||||
|
} |
||||
|
|
||||
|
private void Order() |
||||
|
{ |
||||
|
//TODO: Is there any way that is more performant?
|
||||
|
var orderedItems = this.OrderBy(item => item.Order).ToArray(); |
||||
|
Clear(); |
||||
|
AddRange(orderedItems); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Ui.Navigation.Localization.Resource; |
||||
|
using Volo.Abp.UI.Navigation; |
||||
|
|
||||
|
namespace Volo.Abp.Ui.Navigation |
||||
|
{ |
||||
|
public class DefaultMenuContributor : IMenuContributor |
||||
|
{ |
||||
|
public virtual Task ConfigureMenuAsync(MenuConfigurationContext context) |
||||
|
{ |
||||
|
Configure(context); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
protected virtual void Configure(MenuConfigurationContext context) |
||||
|
{ |
||||
|
var l = context.ServiceProvider |
||||
|
.GetRequiredService<IStringLocalizer<AbpUiNavigationResource>>(); |
||||
|
|
||||
|
if (context.Menu.Name == StandardMenus.Main) |
||||
|
{ |
||||
|
context.Menu.AddItem( |
||||
|
new ApplicationMenuItem( |
||||
|
DefaultMenuNames.Application.Main.Administration, |
||||
|
l["Menu:Administration"], |
||||
|
icon: "fa fa-wrench" |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
namespace Volo.Abp.Ui.Navigation |
||||
|
{ |
||||
|
public static class DefaultMenuNames |
||||
|
{ |
||||
|
public static class Application |
||||
|
{ |
||||
|
public static class Main |
||||
|
{ |
||||
|
public const string Administration = "Abp.Application.Main.Administration"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue