From a56adfed8af0d484abb41aa1ba1b76e5b540dc8b Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sat, 6 Jul 2024 14:09:21 +0200 Subject: [PATCH] Disabled dynamic settings for now --- .../UI/Components/DynamicFormBuilder.razor | 78 ++++++++++++++++ .../Core/UI/Views/Admin/Sys/Settings.razor | 93 +++---------------- .../UI/Views/Admin/Sys/SettingsDisabled.razor | 8 ++ .../Servers/UI/Views/Admin/Images/Index.razor | 2 +- 4 files changed, 99 insertions(+), 82 deletions(-) create mode 100644 Moonlight/Core/UI/Components/DynamicFormBuilder.razor create mode 100644 Moonlight/Core/UI/Views/Admin/Sys/SettingsDisabled.razor diff --git a/Moonlight/Core/UI/Components/DynamicFormBuilder.razor b/Moonlight/Core/UI/Components/DynamicFormBuilder.razor new file mode 100644 index 00000000..8195af4f --- /dev/null +++ b/Moonlight/Core/UI/Components/DynamicFormBuilder.razor @@ -0,0 +1,78 @@ +@using System.ComponentModel +@using System.Linq.Expressions +@using System.Reflection +@typeparam T + + + +@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 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> CreatePropertyAccessExpression(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>(member, parameter); + + return lambda; + } + + // From MoonCore. TODO: Maybe provide this and the above function as mooncore helper + private static bool TryGetAttribute(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; + } +} \ No newline at end of file diff --git a/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor b/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor index 11a925dc..5027387d 100644 --- a/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor +++ b/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor @@ -1,6 +1,4 @@ -@page "/admin/sys/settings" - -@using System.ComponentModel +@using System.ComponentModel @using System.Linq.Expressions @using Moonlight.Core.UI.Components.Navigations @using System.Reflection @@ -89,7 +87,7 @@ else
- + @Content
@@ -102,9 +100,9 @@ else private object? CurrentModel; private string[] SidebarItems = []; private string[] Path = []; - private PropertyInfo[] Properties = []; private LazyLoader? LazyLoader; + private RenderFragment Content; protected override async Task OnParametersSetAsync() { @@ -118,7 +116,6 @@ else if (CurrentModel == null) { SidebarItems = []; - Properties = []; } else { @@ -134,13 +131,6 @@ else ) .Select(x => x.Name) .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); @@ -148,30 +138,6 @@ else if (LazyLoader != null) await LazyLoader.Reload(); } - - private void OnFormConfigure(FastFormConfiguration 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() { @@ -201,7 +167,15 @@ else 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 { @@ -214,47 +188,4 @@ else ConfigService.Reload(); await ToastService.Info("Reloaded configuration from disk"); } - - // Building lambda expressions at runtime using reflection is nice ;3 - public static Expression> 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> lambda = Expression.Lambda>( - castResult, param); - - return lambda; - } - - // From MoonCore. TODO: Maybe provide this and the above function as mooncore helper - private bool TryGetAttribute(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; - } } \ No newline at end of file diff --git a/Moonlight/Core/UI/Views/Admin/Sys/SettingsDisabled.razor b/Moonlight/Core/UI/Views/Admin/Sys/SettingsDisabled.razor new file mode 100644 index 00000000..0469c76f --- /dev/null +++ b/Moonlight/Core/UI/Views/Admin/Sys/SettingsDisabled.razor @@ -0,0 +1,8 @@ +@page "/admin/sys/settings" +@using Moonlight.Core.UI.Components.Navigations + +@attribute [RequirePermission(9999)] + + + + \ No newline at end of file diff --git a/Moonlight/Features/Servers/UI/Views/Admin/Images/Index.razor b/Moonlight/Features/Servers/UI/Views/Admin/Images/Index.razor index 723aa4ca..7fd4dc2b 100644 --- a/Moonlight/Features/Servers/UI/Views/Admin/Images/Index.razor +++ b/Moonlight/Features/Servers/UI/Views/Admin/Images/Index.razor @@ -180,7 +180,7 @@ configuration.AddProperty(x => x.AllocationsNeeded) .WithDefaultComponent() .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) .WithDefaultComponent()