From 708a1c805ae4653ac715374956216f9a55055593 Mon Sep 17 00:00:00 2001 From: Falk Date: Tue, 7 Apr 2026 21:32:16 +0200 Subject: [PATCH 1/3] -fixed bug where using DISPLAY attribute at DBContext Column would hide the column. -added de localization -fixed issues where localization would not apply -added feature that DBContexts can now also be custom named by using DISPLAY attribute --- .../Controllers/CoreAdminDataController.cs | 7 ++ .../DotNetEd.CoreAdmin.csproj | 2 + src/DotNetEd.CoreAdmin/JsonLocalizer.cs | 3 +- src/DotNetEd.CoreAdmin/Translations/de.json | 13 +++ .../CoreAdminMenuViewComponent.cs | 12 +++ .../ViewModels/DataListViewModel.cs | 1 + .../ViewModels/MenuViewModel.cs | 2 + .../Views/CoreAdminData/Index.cshtml | 84 +++++++++++-------- .../Components/CoreAdminMenu/Default.cshtml | 11 +-- 9 files changed, 92 insertions(+), 43 deletions(-) create mode 100644 src/DotNetEd.CoreAdmin/Translations/de.json diff --git a/src/DotNetEd.CoreAdmin/Controllers/CoreAdminDataController.cs b/src/DotNetEd.CoreAdmin/Controllers/CoreAdminDataController.cs index f2572f3..1bfade8 100644 --- a/src/DotNetEd.CoreAdmin/Controllers/CoreAdminDataController.cs +++ b/src/DotNetEd.CoreAdmin/Controllers/CoreAdminDataController.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using System.Reflection; @@ -37,6 +38,12 @@ public IActionResult Index(string id) viewModel.EntityType = dbSetProperty.PropertyType.GetGenericArguments().First(); viewModel.DbSetProperty = dbSetProperty; + if (Attribute.IsDefined(viewModel.EntityType, typeof(DisplayAttribute))) + { + viewModel.DbDisplayName = viewModel.EntityType.GetCustomAttribute().Name; + } + + var dbContextObject = (DbContext)this.HttpContext.RequestServices.GetRequiredService(dbSetEntity.DbContextType); var query = dbContextObject.Set(viewModel.EntityType); diff --git a/src/DotNetEd.CoreAdmin/DotNetEd.CoreAdmin.csproj b/src/DotNetEd.CoreAdmin/DotNetEd.CoreAdmin.csproj index 2f8049e..3d916d6 100644 --- a/src/DotNetEd.CoreAdmin/DotNetEd.CoreAdmin.csproj +++ b/src/DotNetEd.CoreAdmin/DotNetEd.CoreAdmin.csproj @@ -29,6 +29,7 @@ + @@ -37,6 +38,7 @@ + diff --git a/src/DotNetEd.CoreAdmin/JsonLocalizer.cs b/src/DotNetEd.CoreAdmin/JsonLocalizer.cs index 5c276b5..7964b11 100644 --- a/src/DotNetEd.CoreAdmin/JsonLocalizer.cs +++ b/src/DotNetEd.CoreAdmin/JsonLocalizer.cs @@ -36,7 +36,8 @@ public IEnumerable GetAllStrings(bool includeParentCultures) foreach(var culture in cultureKeys) { - var fileName = $"{culture}.json"; + //Firefox usually returns something like: "de,en-US;q=0.9,en;q=0.8" + var fileName = $"{culture.Split(',')[0]}.json"; var key = translations.Keys.FirstOrDefault(k => k.EndsWith(fileName)); if (key != null) { diff --git a/src/DotNetEd.CoreAdmin/Translations/de.json b/src/DotNetEd.CoreAdmin/Translations/de.json new file mode 100644 index 0000000..75428f3 --- /dev/null +++ b/src/DotNetEd.CoreAdmin/Translations/de.json @@ -0,0 +1,13 @@ +{ + "Create": "Erstellen", + "AccessDatabaseEntries": "Zugang der Datenbank auf der linken Seite", + "DevelopmentModeMessage": "Sie befinden sich im Entwicklermodus. Keine Authentisierung wurde für CoreAdmin festgelegt. Core Admin wird außerhalb vom Entwicklermodus nicht erreichbar sein. Siehe Dokumentation https://github.com/edandersen/core-admin.", + "CreateNew": "Neu Erstellen", + "GoBack": "Zurück", + "Edit": "Bearbeiten", + "Delete": "Löschen", + "DeleteConfirm": "Bestätige Löschung", + "Auto": "Auto", + "Dark": "Dunkel", + "Light": "Hell" +} diff --git a/src/DotNetEd.CoreAdmin/ViewComponents/CoreAdminMenuViewComponent.cs b/src/DotNetEd.CoreAdmin/ViewComponents/CoreAdminMenuViewComponent.cs index a38e947..e21bd1d 100644 --- a/src/DotNetEd.CoreAdmin/ViewComponents/CoreAdminMenuViewComponent.cs +++ b/src/DotNetEd.CoreAdmin/ViewComponents/CoreAdminMenuViewComponent.cs @@ -1,6 +1,9 @@ using DotNetEd.CoreAdmin.ViewModels; using Microsoft.AspNetCore.Mvc; +using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; using System.Threading.Tasks; namespace DotNetEd.CoreAdmin.ViewComponents @@ -22,6 +25,15 @@ public IViewComponentResult Invoke() { viewModel.DbContextNames.Add(dbSetEntity.DbContextType.Name); viewModel.DbSetNames.Add(dbSetEntity.Name); + + var d = dbSetEntity.Name; + if (Attribute.IsDefined(dbSetEntity.UnderlyingType, typeof(DisplayAttribute))) + { + var displayAttribute = dbSetEntity.UnderlyingType.GetCustomAttributes(typeof(DisplayAttribute), true).FirstOrDefault() as DisplayAttribute; + if (!String.IsNullOrEmpty(displayAttribute.Name)) + d = displayAttribute.Name; + } + viewModel.DbDisplayNames.Add(d); } return View(viewModel); diff --git a/src/DotNetEd.CoreAdmin/ViewModels/DataListViewModel.cs b/src/DotNetEd.CoreAdmin/ViewModels/DataListViewModel.cs index 1ef710c..bda00d5 100644 --- a/src/DotNetEd.CoreAdmin/ViewModels/DataListViewModel.cs +++ b/src/DotNetEd.CoreAdmin/ViewModels/DataListViewModel.cs @@ -11,5 +11,6 @@ public class DataListViewModel public IEnumerable Data { get; internal set; } public DbContext DbContext { get; internal set; } public PropertyInfo DbSetProperty { get; internal set; } + public string DbDisplayName { get; internal set; } } } diff --git a/src/DotNetEd.CoreAdmin/ViewModels/MenuViewModel.cs b/src/DotNetEd.CoreAdmin/ViewModels/MenuViewModel.cs index 4646caf..afb87fa 100644 --- a/src/DotNetEd.CoreAdmin/ViewModels/MenuViewModel.cs +++ b/src/DotNetEd.CoreAdmin/ViewModels/MenuViewModel.cs @@ -6,5 +6,7 @@ public class MenuViewModel { public List DbContextNames { get; set; } = new List(); public List DbSetNames { get; set; } = new List(); + + public List DbDisplayNames { get; set; } = new List(); } } diff --git a/src/DotNetEd.CoreAdmin/Views/CoreAdminData/Index.cshtml b/src/DotNetEd.CoreAdmin/Views/CoreAdminData/Index.cshtml index ae976f5..e2669af 100644 --- a/src/DotNetEd.CoreAdmin/Views/CoreAdminData/Index.cshtml +++ b/src/DotNetEd.CoreAdmin/Views/CoreAdminData/Index.cshtml @@ -16,7 +16,7 @@ var options = CoreAdminOptions.FirstOrDefault(); } -

@Model.DbSetProperty.Name

+

@Model.DbDisplayName

@@ -24,7 +24,8 @@
-@(Html +@( +Html .Grid(Model.Data) .Build((columns) => { @@ -35,34 +36,44 @@ var changedType = Expression.Convert(entity, Model.EntityType); var property = Expression.Property(changedType, entityProperty.Name); var propertyDisplayName = entityProperty.Name; - if(Attribute.IsDefined(entityProperty, typeof(DisplayAttribute))) + + + if (Attribute.IsDefined(entityProperty, typeof(DisplayAttribute))) { - var displayAttribute = entityProperty.GetCustomAttributes(typeof(DisplayAttribute),true).FirstOrDefault() as DisplayAttribute; - if(displayAttribute != null) + var displayAttribute = entityProperty.GetCustomAttributes(typeof(DisplayAttribute), true).FirstOrDefault() as DisplayAttribute; + if (displayAttribute != null) { var displayAutoGeneratedField = displayAttribute.GetAutoGenerateField(); - if(!( displayAutoGeneratedField.HasValue? displayAutoGeneratedField.Value : false )) + if (!(displayAutoGeneratedField.HasValue ? displayAutoGeneratedField.Value : false)) continue; - if(!string.IsNullOrEmpty(displayAttribute.Description)) - propertyDisplayName = displayAttribute.Description; + if (!string.IsNullOrEmpty(displayAttribute.Name)) + propertyDisplayName = displayAttribute.Name; } - } - else if (entityProperty.PropertyType == typeof(Boolean)) + } + + if (entityProperty.PropertyType == typeof(Boolean)) { - var lambda = Expression.Lambda>(property, entity); - columns.Add(lambda).RenderedAs(m=> Html.CheckBox(entityProperty.Name, lambda.Compile().Invoke(m))).Titled(propertyDisplayName); + var l = Expression.Lambda>(property, entity); + columns.Add(l).RenderedAs(m => Html.CheckBox(entityProperty.Name, l.Compile().Invoke(m), new { onclick = "return false;", style = "cursor: default;" })).Titled(propertyDisplayName); + continue; } - else if (entityProperty.PropertyType == typeof(byte[])) + + + if (entityProperty.PropertyType == typeof(byte[])) { - var lambda = Expression.Lambda>(property, entity); + var l = Expression.Lambda>(property, entity); - columns.Add(lambda).Titled(entityProperty.Name) - .RenderedAs((value) => { - var base64 = ImageUtils.WebBase64EncodeImageByteArrayOrNull(lambda.Compile().Invoke(value)); + columns.Add(l).Titled(entityProperty.Name) + .RenderedAs((value) => + { + var base64 = ImageUtils.WebBase64EncodeImageByteArrayOrNull(l.Compile().Invoke(value)); return base64 == null ? string.Empty : $""; - } ).Encoded(false); - } - /* if (entityProperty.PropertyType == typeof(Guid)) + }).Encoded(false); + continue; + } + + + /* if (entityProperty.PropertyType == typeof(Guid)) { var lambda = Expression.Lambda>(property, entity); @@ -128,23 +139,22 @@ var lambda = Expression.Lambda>(property, entity); columns.Add(lambda).Titled(propertyDisplayName); }*/ - else - { - //Use reflection to build up lambda expression and call columns.Add(...) and Titled(...) for any field that can be rendered as text. - //This is equivalent to: - //if(entityProperty.PropertyType == typeof(Foo?)){ - // var lambda = Expression.Lambda>(property, entity); - // columns.Add(lambda).Titled(propertyDisplayName); - //} - //But avoids needing an else if for each possible type, nullable types, etc.. - var funcType = typeof(Func<,>).MakeGenericType(typeof(object),entityProperty.PropertyType); - var lambdaMethod = typeof(Expression).GetMethods(BindingFlags.Public|BindingFlags.Static).Where(m=>m.Name == "Lambda" && m.IsGenericMethod).First().MakeGenericMethod(funcType); - var lambda = lambdaMethod.Invoke(null,new object[]{property,new ParameterExpression[]{entity}}); - var addMethod = columns.GetType().GetMethods(BindingFlags.Public|BindingFlags.Instance).Where(m=>m.Name=="Add" && m.IsGenericMethod).First(); - var column = addMethod.MakeGenericMethod(entityProperty.PropertyType).Invoke(columns,new object[]{lambda}); - var titledMethod = typeof(GridColumnExtensions).GetMethods(BindingFlags.Static|BindingFlags.Public).Where(m=>m.Name=="Titled").First().MakeGenericMethod(typeof(object),entityProperty.PropertyType); - titledMethod.Invoke(null, new[]{column, propertyDisplayName}); - } + + //Use reflection to build up lambda expression and call columns.Add(...) and Titled(...) for any field that can be rendered as text. + //This is equivalent to: + //if(entityProperty.PropertyType == typeof(Foo?)){ + // var lambda = Expression.Lambda>(property, entity); + // columns.Add(lambda).Titled(propertyDisplayName); + //} + //But avoids needing an else if for each possible type, nullable types, etc.. + var funcType = typeof(Func<,>).MakeGenericType(typeof(object),entityProperty.PropertyType); + var lambdaMethod = typeof(Expression).GetMethods(BindingFlags.Public|BindingFlags.Static).Where(m=>m.Name == "Lambda" && m.IsGenericMethod).First().MakeGenericMethod(funcType); + var lambda = lambdaMethod.Invoke(null,new object[]{property,new ParameterExpression[]{entity}}); + var addMethod = columns.GetType().GetMethods(BindingFlags.Public|BindingFlags.Instance).Where(m=>m.Name=="Add" && m.IsGenericMethod).First(); + var column = addMethod.MakeGenericMethod(entityProperty.PropertyType).Invoke(columns,new object[]{lambda}); + var titledMethod = typeof(GridColumnExtensions).GetMethods(BindingFlags.Static|BindingFlags.Public).Where(m=>m.Name=="Titled").First().MakeGenericMethod(typeof(object),entityProperty.PropertyType); + titledMethod.Invoke(null, new[]{column, propertyDisplayName}); + } // only supports single PKs, not composite ones diff --git a/src/DotNetEd.CoreAdmin/Views/Shared/Components/CoreAdminMenu/Default.cshtml b/src/DotNetEd.CoreAdmin/Views/Shared/Components/CoreAdminMenu/Default.cshtml index 14d2c04..704f099 100644 --- a/src/DotNetEd.CoreAdmin/Views/Shared/Components/CoreAdminMenu/Default.cshtml +++ b/src/DotNetEd.CoreAdmin/Views/Shared/Components/CoreAdminMenu/Default.cshtml @@ -1,4 +1,5 @@ -@model DotNetEd.CoreAdmin.ViewModels.MenuViewModel + +@model DotNetEd.CoreAdmin.ViewModels.MenuViewModel