Disabled dynamic settings for now
This commit is contained in:
78
Moonlight/Core/UI/Components/DynamicFormBuilder.razor
Normal file
78
Moonlight/Core/UI/Components/DynamicFormBuilder.razor
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
@using System.ComponentModel
|
||||||
|
@using System.Linq.Expressions
|
||||||
|
@using System.Reflection
|
||||||
|
@typeparam T
|
||||||
|
|
||||||
|
<FastForm TModel="T" Model="(T)Model" OnConfigure="OnFormConfigure"/>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[Parameter] public object Model { get; set; }
|
||||||
|
|
||||||
|
private PropertyInfo[] Properties;
|
||||||
|
|
||||||
|
protected override async Task OnParametersSetAsync()
|
||||||
|
{
|
||||||
|
Properties = Model.GetType().GetProperties()
|
||||||
|
.Where(x =>
|
||||||
|
!x.PropertyType.Namespace.StartsWith("Moonlight") &&
|
||||||
|
DefaultComponentRegistry.Get(x.PropertyType) != null // Check if a component has been registered for that type
|
||||||
|
)
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFormConfigure(FastFormConfiguration<T> configuration)
|
||||||
|
{
|
||||||
|
if (Model == null) // This will technically never be true because of the ui logic
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var property in Properties)
|
||||||
|
{
|
||||||
|
var propertyFunc = GetType().GetMethod("CreatePropertyAccessExpression")!.MakeGenericMethod(property.PropertyType).Invoke(this, [property]);
|
||||||
|
var config = configuration.GetType().GetMethod("AddProperty")!.MakeGenericMethod(property.PropertyType).Invoke(configuration, [propertyFunc])!;
|
||||||
|
|
||||||
|
config.GetType().GetMethod("WithDefaultComponent")!.Invoke(config, []);
|
||||||
|
|
||||||
|
var attributes = property.GetCustomAttributes(true);
|
||||||
|
|
||||||
|
if (TryGetAttribute(attributes, out DisplayNameAttribute nameAttribute))
|
||||||
|
config.GetType().GetMethod("WithName")!.Invoke(config, [nameAttribute.DisplayName]);
|
||||||
|
|
||||||
|
if (TryGetAttribute(attributes, out DescriptionAttribute descriptionAttribute))
|
||||||
|
config.GetType().GetMethod("WithDescription")!.Invoke(config, [descriptionAttribute.Description]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Building lambda expressions at runtime using reflection is nice ;3
|
||||||
|
public static Expression<Func<T, TResult?>> CreatePropertyAccessExpression<TResult>(PropertyInfo property)
|
||||||
|
{
|
||||||
|
// Create a parameter expression for TData
|
||||||
|
var parameter = Expression.Parameter(typeof(T), "data");
|
||||||
|
|
||||||
|
// Access the property
|
||||||
|
var member = Expression.MakeMemberAccess(parameter, property);
|
||||||
|
|
||||||
|
// Create the lambda expression
|
||||||
|
var lambda = Expression.Lambda<Func<T, TResult?>>(member, parameter);
|
||||||
|
|
||||||
|
return lambda;
|
||||||
|
}
|
||||||
|
|
||||||
|
// From MoonCore. TODO: Maybe provide this and the above function as mooncore helper
|
||||||
|
private static bool TryGetAttribute<T>(object[] attributes, out T result) where T : Attribute
|
||||||
|
{
|
||||||
|
var searchType = typeof(T);
|
||||||
|
|
||||||
|
var attr = attributes
|
||||||
|
.FirstOrDefault(x => x.GetType() == searchType);
|
||||||
|
|
||||||
|
if (attr == null)
|
||||||
|
{
|
||||||
|
result = default!;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = (attr as T)!;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
@page "/admin/sys/settings"
|
@using System.ComponentModel
|
||||||
|
|
||||||
@using System.ComponentModel
|
|
||||||
@using System.Linq.Expressions
|
@using System.Linq.Expressions
|
||||||
@using Moonlight.Core.UI.Components.Navigations
|
@using Moonlight.Core.UI.Components.Navigations
|
||||||
@using System.Reflection
|
@using System.Reflection
|
||||||
@@ -89,7 +87,7 @@ else
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-9 col-12">
|
<div class="col-md-9 col-12">
|
||||||
<LazyLoader @ref="LazyLoader" Load="Load" UseDefaultValues="false" TimeUntilSpinnerIsShown="TimeSpan.Zero">
|
<LazyLoader @ref="LazyLoader" Load="Load" UseDefaultValues="false" TimeUntilSpinnerIsShown="TimeSpan.Zero">
|
||||||
<FastForm Model="CurrentModel" OnConfigure="OnFormConfigure"/>
|
@Content
|
||||||
</LazyLoader>
|
</LazyLoader>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,9 +100,9 @@ else
|
|||||||
private object? CurrentModel;
|
private object? CurrentModel;
|
||||||
private string[] SidebarItems = [];
|
private string[] SidebarItems = [];
|
||||||
private string[] Path = [];
|
private string[] Path = [];
|
||||||
private PropertyInfo[] Properties = [];
|
|
||||||
|
|
||||||
private LazyLoader? LazyLoader;
|
private LazyLoader? LazyLoader;
|
||||||
|
private RenderFragment Content;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
@@ -118,7 +116,6 @@ else
|
|||||||
if (CurrentModel == null)
|
if (CurrentModel == null)
|
||||||
{
|
{
|
||||||
SidebarItems = [];
|
SidebarItems = [];
|
||||||
Properties = [];
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -134,13 +131,6 @@ else
|
|||||||
)
|
)
|
||||||
.Select(x => x.Name)
|
.Select(x => x.Name)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
Properties = props
|
|
||||||
.Where(x =>
|
|
||||||
!x.PropertyType.Namespace.StartsWith("Moonlight") &&
|
|
||||||
DefaultComponentRegistry.Get(x.PropertyType) != null // Check if a component has been registered for that type
|
|
||||||
)
|
|
||||||
.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
@@ -148,30 +138,6 @@ else
|
|||||||
if (LazyLoader != null)
|
if (LazyLoader != null)
|
||||||
await LazyLoader.Reload();
|
await LazyLoader.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFormConfigure(FastFormConfiguration<object> configuration)
|
|
||||||
{
|
|
||||||
if(CurrentModel == null) // This will technically never be true because of the ui logic
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var property in Properties)
|
|
||||||
{
|
|
||||||
var propConfig = configuration
|
|
||||||
.AddProperty(CreatePropertyAccessExpression(property))
|
|
||||||
.WithDefaultComponent();
|
|
||||||
|
|
||||||
var customAttributes = property.GetCustomAttributes(false);
|
|
||||||
|
|
||||||
if(customAttributes.Length == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (TryGetAttribute(customAttributes, out DisplayNameAttribute nameAttribute))
|
|
||||||
propConfig.WithName(nameAttribute.DisplayName);
|
|
||||||
|
|
||||||
if (TryGetAttribute(customAttributes, out DescriptionAttribute descriptionAttribute))
|
|
||||||
propConfig.WithDescription(descriptionAttribute.Description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetBackPath()
|
private string GetBackPath()
|
||||||
{
|
{
|
||||||
@@ -201,7 +167,15 @@ else
|
|||||||
return Resolve(prop.GetValue(model)!, path, index + 1);
|
return Resolve(prop.GetValue(model)!, path, index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task Load(LazyLoader _) => Task.CompletedTask; // Seems useless, it more or less is, but it shows a nice loading ui while the form changes
|
private Task Load(LazyLoader _)
|
||||||
|
{
|
||||||
|
Content = ComponentHelper.FromType(typeof(DynamicFormBuilder<>).MakeGenericType(CurrentModel.GetType()), parameters =>
|
||||||
|
{
|
||||||
|
parameters.Add("Model", CurrentModel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task Save() // Saves all changes to disk, all changes are live updated as the config service reference will be edited directly
|
private async Task Save() // Saves all changes to disk, all changes are live updated as the config service reference will be edited directly
|
||||||
{
|
{
|
||||||
@@ -214,47 +188,4 @@ else
|
|||||||
ConfigService.Reload();
|
ConfigService.Reload();
|
||||||
await ToastService.Info("Reloaded configuration from disk");
|
await ToastService.Info("Reloaded configuration from disk");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Building lambda expressions at runtime using reflection is nice ;3
|
|
||||||
public static Expression<Func<object, object?>> CreatePropertyAccessExpression(PropertyInfo property)
|
|
||||||
{
|
|
||||||
// Get the type that declares the property
|
|
||||||
Type declaringType = property.DeclaringType!;
|
|
||||||
|
|
||||||
// Create a parameter expression for the input object
|
|
||||||
ParameterExpression param = Expression.Parameter(typeof(object), "obj");
|
|
||||||
|
|
||||||
// Create an expression to cast the input object to the declaring type
|
|
||||||
UnaryExpression cast = Expression.Convert(param, declaringType);
|
|
||||||
|
|
||||||
// Create an expression to access the property
|
|
||||||
MemberExpression propertyAccess = Expression.Property(cast, property);
|
|
||||||
|
|
||||||
// Create an expression to cast the property value to object
|
|
||||||
UnaryExpression castResult = Expression.Convert(propertyAccess, typeof(object));
|
|
||||||
|
|
||||||
// Create the final lambda expression
|
|
||||||
Expression<Func<object, object?>> lambda = Expression.Lambda<Func<object, object?>>(
|
|
||||||
castResult, param);
|
|
||||||
|
|
||||||
return lambda;
|
|
||||||
}
|
|
||||||
|
|
||||||
// From MoonCore. TODO: Maybe provide this and the above function as mooncore helper
|
|
||||||
private bool TryGetAttribute<T>(object[] attributes, out T result) where T : Attribute
|
|
||||||
{
|
|
||||||
var searchType = typeof(T);
|
|
||||||
|
|
||||||
var attr = attributes
|
|
||||||
.FirstOrDefault(x => x.GetType() == searchType);
|
|
||||||
|
|
||||||
if (attr == null)
|
|
||||||
{
|
|
||||||
result = default!;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = (attr as T)!;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
8
Moonlight/Core/UI/Views/Admin/Sys/SettingsDisabled.razor
Normal file
8
Moonlight/Core/UI/Views/Admin/Sys/SettingsDisabled.razor
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
@page "/admin/sys/settings"
|
||||||
|
@using Moonlight.Core.UI.Components.Navigations
|
||||||
|
|
||||||
|
@attribute [RequirePermission(9999)]
|
||||||
|
|
||||||
|
<AdminSysNavigation Index="1"/>
|
||||||
|
|
||||||
|
<NotImplementedAlert />
|
||||||
@@ -180,7 +180,7 @@
|
|||||||
configuration.AddProperty(x => x.AllocationsNeeded)
|
configuration.AddProperty(x => x.AllocationsNeeded)
|
||||||
.WithDefaultComponent()
|
.WithDefaultComponent()
|
||||||
.WithPage("Miscellaneous")
|
.WithPage("Miscellaneous")
|
||||||
.WithValidation(x => x > 1 ? ValidationResult.Success : new ValidationResult("This specifies the amount of allocations needed for this image in order to create a server"));
|
.WithValidation(x => x >= 1 ? ValidationResult.Success : new ValidationResult("This specifies the amount of allocations needed for this image in order to create a server"));
|
||||||
|
|
||||||
configuration.AddProperty(x => x.InstallDockerImage)
|
configuration.AddProperty(x => x.InstallDockerImage)
|
||||||
.WithDefaultComponent()
|
.WithDefaultComponent()
|
||||||
|
|||||||
Reference in New Issue
Block a user