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; |
|||
using System.Linq; |
|||
using Volo.Utils.SolutionTemplating.Files; |
|||
|
|||
namespace Volo.Utils.SolutionTemplating.Building |
|||
namespace Volo.Utils.SolutionTemplating.Building |
|||
{ |
|||
public abstract class ProjectBuildPipelineStep |
|||
{ |
|||
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); |
|||
} |
|||
|
|||
@ -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); |
|||
} |
|||
|
|||
@ -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.MultiTenancy; |
|||
using Volo.Abp.Validation; |
|||
|
|||
namespace Volo.Abp.Features |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpLocalizationAbstractionsModule), |
|||
typeof(AbpMultiTenancyAbstractionsModule) |
|||
typeof(AbpMultiTenancyAbstractionsModule), |
|||
typeof(AbpValidationModule) |
|||
)] |
|||
public class AbpFeaturesModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.OnRegistred(FeatureInterceptorRegistrar.RegisterIfNeeded); |
|||
AutoAddDefinitionProviders(context.Services); |
|||
} |
|||
|
|||
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.Immutable; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace Volo.Abp.Features |
|||
{ |
|||
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() |
|||
{ |
|||
return Features.Values.ToImmutableList(); |
|||
if (Groups.ContainsKey(name)) |
|||
{ |
|||
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 |
|||
{ |
|||
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 |
|||
{ |
|||
[DependsOn(typeof(AbpUiModule))] |
|||
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