Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,4 @@ MigrationBackup/
/src/DotNetEd.CoreAdmin/out/*
/src/DotNetEd.CoreAdmin/out/*
/src/DotNetEd.CoreAdmin/out/*
.idea/
250 changes: 128 additions & 122 deletions src/DotNetEd.CoreAdmin/Controllers/CoreAdminDataController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,113 @@ public IActionResult Index(string id)
return View(viewModel);
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "<Pending>")]
private object GetDbSetValueOrNull(string dbSetName, out DbContext dbContextObject,
out Type typeOfEntity,
out Dictionary<string, Dictionary<object, string>> relationships)
{
string dbSetPropertyName = dbSetName;
var stringParts = dbSetName.Split(" - ");
if (stringParts.Length > 0)
{
dbSetPropertyName = stringParts[stringParts.Length - 1];
}

foreach (var dbSetEntity in dbSetEntities.Where(db => db.Name.ToLowerInvariant() == dbSetName.ToLowerInvariant()))
{
foreach (var dbSetProperty in dbSetEntity.DbContextType.GetProperties())
{
if (dbSetProperty.PropertyType.IsGenericType && dbSetProperty.PropertyType.Name.StartsWith("DbSet") && dbSetProperty.Name.ToLowerInvariant() == dbSetPropertyName.ToLowerInvariant())
{
dbContextObject = (DbContext)this.HttpContext.RequestServices.GetRequiredService(dbSetEntity.DbContextType);
if (dbSetEntity.ConnectionString != null)
{
dbContextObject.Database.GetDbConnection().ConnectionString = dbSetEntity.ConnectionString;
}

typeOfEntity = dbSetProperty.PropertyType.GetGenericArguments()[0];


var fks = dbContextObject.Model.FindEntityType(typeOfEntity)
.GetForeignKeyProperties().Cast<Microsoft.EntityFrameworkCore.Metadata.RuntimeProperty>();

var relationshipDictionary = new Dictionary<string, Dictionary<object, string>>();
foreach (var f in fks)
{
var childValues = new Dictionary<object, string>();

if (f.ForeignKeys.Count == 1)
{
var typeOfChild = f.ForeignKeys.First();

var targetChildListOnDbContext = dbContextObject.GetType().GetProperties()
.FirstOrDefault(p => p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)
&& p.PropertyType.GetGenericArguments().First().FullName == typeOfChild.PrincipalEntityType.Name);

var primaryKey2 = dbContextObject.Model.FindEntityType(typeOfChild.PrincipalEntityType.Name).FindPrimaryKey();

if (primaryKey2.Properties.Count > 1)
{
continue;
}

var allChildren2 = (IEnumerable<object>)dbContextObject.GetType().GetProperty(targetChildListOnDbContext.Name).GetValue(dbContextObject);

NullabilityInfoContext _nullabilityContext = new NullabilityInfoContext();
var nullabilityInfo = _nullabilityContext.Create(typeOfEntity.GetProperty(f.Name));
if (nullabilityInfo.WriteState == NullabilityState.Nullable)
{
childValues.Add(string.Empty, String.Empty);
}

foreach (var childValue in allChildren2)
{
var childPkValue = childValue.GetType().GetProperty(primaryKey2.Properties.First().Name).GetValue(childValue);
childValues.TryAdd(childPkValue, $"[{childPkValue}] {childValue}");
}
}


relationshipDictionary.Add(f.Name, childValues);
}

relationships = relationshipDictionary;

return dbSetProperty.GetValue(dbContextObject);
}
}
}

dbContextObject = null;
typeOfEntity = null;
relationships = null;
return null;
}

private object GetEntityFromDbSet(string dbSetName, string id, string secondId,
out DbContext dbContextObject, out Type typeOfEntity,
out Dictionary<string, Dictionary<object, string>> relationships)
{
var dbSetValue = GetDbSetValueOrNull(dbSetName, out dbContextObject, out typeOfEntity, out relationships);

var primaryKeyList = new List<object>();

var primaryKey = dbContextObject.Model.FindEntityType(typeOfEntity).FindPrimaryKey();
var clrType = primaryKey.Properties[0].ClrType;
object convertedPrimaryKey = GetConvertedPrimaryKey(clrType, id);

primaryKeyList.Add(convertedPrimaryKey);

if (secondId != null)
{
clrType = primaryKey.Properties[1].ClrType;
object convertedSecondPrimaryKey = GetConvertedPrimaryKey(clrType, secondId);
primaryKeyList.Add(convertedSecondPrimaryKey);
}

return dbSetValue.GetType().InvokeMember("Find", BindingFlags.InvokeMethod, null, dbSetValue, args: primaryKeyList.ToArray());
}

[HttpPost]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> CreateEntityPost(string dbSetName, string id, [FromForm] object formData)
Expand All @@ -95,6 +202,9 @@ await TryUpdateModelAsync(newEntity, entityType, string.Empty,
await CompositeValueProvider.CreateAsync(this.ControllerContext, this.ControllerContext.ValueProviderFactories),
(ModelMetadata meta) => !databaseGeneratedProperties.Contains(meta.PropertyName));

newEntity.GetType().GetProperties().Where(p => p.PropertyType == typeof(DateTimeOffset)).ToList().ForEach(p =>
p.SetValue(newEntity, ((DateTimeOffset)p.GetValue(newEntity)).ToUniversalTime()));

newEntity.GetType().GetProperties()
.Where(p => p.GetCustomAttributes().Any(a => a.GetType().Name.Contains("ForeignKey"))).Select(p => p.Name).ToList().ForEach(fkProperty =>
{
Expand Down Expand Up @@ -179,6 +289,9 @@ public async Task<IActionResult> EditEntityPost(string dbSetName, string id, str
await TryUpdateModelAsync(entityToEdit, entityType, string.Empty, await CompositeValueProvider.CreateAsync(this.ControllerContext, this.ControllerContext.ValueProviderFactories),
(ModelMetadata meta) => !databaseGeneratedProperties.Contains(meta.PropertyName));

entityToEdit.GetType().GetProperties().Where(p => p.PropertyType == typeof(DateTimeOffset)).ToList().ForEach(p =>
p.SetValue(entityToEdit, ((DateTimeOffset)p.GetValue(entityToEdit)).ToUniversalTime()));

entityToEdit.GetType().GetProperties()
.Where(p => p.GetCustomAttributes().Any(a => a.GetType().Name.Contains("ForeignKey"))).Select(p => p.Name).ToList().ForEach(fkProperty =>
{
Expand All @@ -203,6 +316,21 @@ await TryUpdateModelAsync(entityToEdit, entityType, string.Empty, await Composit
return View("Edit", entityToEdit);
}

private async Task AddByteArrayFiles(object entityToEdit)
{
foreach (var file in Request.Form.Files)
{
var matchingProperty = entityToEdit.GetType().GetProperties()
.FirstOrDefault(prop => prop.Name == file.Name && prop.PropertyType == typeof(byte[]));
if (matchingProperty != null)
{
var memoryStream = new MemoryStream();
await file.CopyToAsync(memoryStream);
matchingProperty.SetValue(entityToEdit, memoryStream.ToArray());
}
}
}

[HttpGet]
public IActionResult DeleteEntity(string dbSetName, string id, string secondId)
{
Expand Down Expand Up @@ -285,127 +413,5 @@ private static object GetConvertedPrimaryKey(Type clrType, string id)

return convertedPrimaryKey;
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "<Pending>")]
private object GetDbSetValueOrNull(string dbSetName, out DbContext dbContextObject,
out Type typeOfEntity,
out Dictionary<string, Dictionary<object, string>> relationships)
{
string dbSetPropertyName = dbSetName;
var stringParts = dbSetName.Split(" - ");
if (stringParts.Length > 0)
{
dbSetPropertyName = stringParts[stringParts.Length - 1];
}

foreach (var dbSetEntity in dbSetEntities.Where(db => db.Name.ToLowerInvariant() == dbSetName.ToLowerInvariant()))
{
foreach (var dbSetProperty in dbSetEntity.DbContextType.GetProperties())
{
if (dbSetProperty.PropertyType.IsGenericType && dbSetProperty.PropertyType.Name.StartsWith("DbSet") && dbSetProperty.Name.ToLowerInvariant() == dbSetPropertyName.ToLowerInvariant())
{
dbContextObject = (DbContext)this.HttpContext.RequestServices.GetRequiredService(dbSetEntity.DbContextType);
if (dbSetEntity.ConnectionString != null)
{
dbContextObject.Database.GetDbConnection().ConnectionString = dbSetEntity.ConnectionString;
}

typeOfEntity = dbSetProperty.PropertyType.GetGenericArguments()[0];


var fks = dbContextObject.Model.FindEntityType(typeOfEntity)
.GetForeignKeyProperties().Cast<Microsoft.EntityFrameworkCore.Metadata.RuntimeProperty>();

var relationshipDictionary = new Dictionary<string, Dictionary<object, string>>();
foreach (var f in fks)
{
var childValues = new Dictionary<object, string>();

if (f.ForeignKeys.Count == 1)
{
var typeOfChild = f.ForeignKeys[0];

var targetChildListOnDbContext = dbContextObject.GetType().GetProperties()
.FirstOrDefault(p => p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)
&& p.PropertyType.GetGenericArguments().First().FullName == typeOfChild.PrincipalEntityType.Name);

var primaryKey2 = dbContextObject.Model.FindEntityType(typeOfChild.PrincipalEntityType.Name).FindPrimaryKey();

if (primaryKey2.Properties.Count > 1)
{
continue;
}

var allChildren2 = (IEnumerable<object>)dbContextObject.GetType().GetProperty(targetChildListOnDbContext.Name).GetValue(dbContextObject);

NullabilityInfoContext _nullabilityContext = new NullabilityInfoContext();
var nullabilityInfo = _nullabilityContext.Create(typeOfEntity.GetProperty(f.Name));
if (nullabilityInfo.WriteState == NullabilityState.Nullable)
{
childValues.Add(string.Empty, String.Empty);
}

foreach (var childValue in allChildren2)
{
var childPkValue = childValue.GetType().GetProperty(primaryKey2.Properties.First().Name).GetValue(childValue);
childValues.TryAdd(childPkValue, $"[{childPkValue}] {childValue}");
}
}


relationshipDictionary.Add(f.Name, childValues);
}

relationships = relationshipDictionary;

return dbSetProperty.GetValue(dbContextObject);
}
}
}

dbContextObject = null;
typeOfEntity = null;
relationships = null;
return null;
}

private object GetEntityFromDbSet(string dbSetName, string id, string secondId,
out DbContext dbContextObject, out Type typeOfEntity,
out Dictionary<string, Dictionary<object, string>> relationships)
{
var dbSetValue = GetDbSetValueOrNull(dbSetName, out dbContextObject, out typeOfEntity, out relationships);

var primaryKeyList = new List<object>();

var primaryKey = dbContextObject.Model.FindEntityType(typeOfEntity).FindPrimaryKey();
var clrType = primaryKey.Properties[0].ClrType;
object convertedPrimaryKey = GetConvertedPrimaryKey(clrType, id);

primaryKeyList.Add(convertedPrimaryKey);

if (secondId != null)
{
clrType = primaryKey.Properties[1].ClrType;
object convertedSecondPrimaryKey = GetConvertedPrimaryKey(clrType, secondId);
primaryKeyList.Add(convertedSecondPrimaryKey);
}

return dbSetValue.GetType().InvokeMember("Find", BindingFlags.InvokeMethod, null, dbSetValue, args: primaryKeyList.ToArray());
}

private async Task AddByteArrayFiles(object entityToEdit)
{
foreach (var file in Request.Form.Files)
{
var matchingProperty = entityToEdit.GetType().GetProperties()
.FirstOrDefault(prop => prop.Name == file.Name && prop.PropertyType == typeof(byte[]));
if (matchingProperty != null)
{
var memoryStream = new MemoryStream();
await file.CopyToAsync(memoryStream);
matchingProperty.SetValue(entityToEdit, memoryStream.ToArray());
}
}
}
}
}
27 changes: 15 additions & 12 deletions src/DotNetEd.CoreAdmin/DotNetEd.CoreAdmin.csproj
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<TargetFrameworks>net8.0</TargetFrameworks>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<PreserveCompilationContext>false</PreserveCompilationContext>
<SuppressDependenciesWhenPacking>false</SuppressDependenciesWhenPacking>
<PackageId>AmplifiAdminPortal</PackageId>
<PackageVersion>1.0.19</PackageVersion>
<Description>Automagically add an Admin Panel to your .NET 6 web app.</Description>
<PackageVersion>3.0.1</PackageVersion>
<Description>Automagically add an Admin Panel to your .NET 8 web app.</Description>
<Title>Core Admin Panel for ASP.NET Core</Title>
<Authors>amplifi</Authors>
<Copyright>Copyright ©2022 Ed Andersen</Copyright>
<Copyright>Copyright ©2024 Ed Andersen</Copyright>
<PackageProjectUrl>https://github.com/amplified-global/core-admin</PackageProjectUrl>
<PackageLicenseExpression>LGPL-3.0-or-later</PackageLicenseExpression>
<Nullable>disable</Nullable>
Expand All @@ -28,29 +28,32 @@
<ItemGroup>
<Content Remove="Translations\en-us.json" />
<Content Remove="Translations\fr-FR.json" />
<Content Remove="Translations\it-IT.json" />
<Content Remove="Translations\ja-JP.json" />
<Content Remove="Translations\pt-BR.json" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Translations\it-IT.json" />
<EmbeddedResource Include="Translations\en-US.json" />
<EmbeddedResource Include="Translations\fr-FR.json" />
<EmbeddedResource Include="Translations\ja-JP.json" />
<EmbeddedResource Include="Translations\pt-BR.json" />
</ItemGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
<PackageReference Include="NonFactors.Grid.Core.Mvc6" Version="8.0.0" />
<PackageReference Include="FirebaseAuthentication.net" Version="3.7.2" />
<PackageReference Include="GoogleAuthenticator" Version="3.0.0" />
<PackageReference Condition="'$(TargetFramework)' == 'net6.0'" Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
<PackageReference Condition="'$(TargetFramework)' == 'net7.0'" Include="Microsoft.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
<PackageReference Include="NonFactors.Grid.Core.Mvc6" Version="7.1.0" />
<PackageReference Include="Npgsql" Version="6.0.9" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.4" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.25.0" />
<PackageReference Include="GoogleAuthenticator" Version="3.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.1" />
<PackageReference Include="Npgsql" Version="9.0.2" />
<PackageReference Include="System.Data.SqlClient" Version="4.9.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.1.2" />
</ItemGroup>


Expand Down
5 changes: 4 additions & 1 deletion src/DotNetEd.CoreAdmin/Translations/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@
"GoBack": "Go back",
"Edit": "Edit",
"Delete": "Delete",
"DeleteConfirm": "Are you sure you want to delete this?"
"DeleteConfirm": "Are you sure you want to delete this?",
"Auto": "Auto",
"Dark": "Dark",
"Light": "Light"
}
5 changes: 4 additions & 1 deletion src/DotNetEd.CoreAdmin/Translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@
"GoBack": "Retour",
"Edit": "Modifier",
"Delete": "Supprimer",
"DeleteConfirm": "Êtes-vous sûr de vouloir le supprimer ?"
"DeleteConfirm": "Êtes-vous sûr de vouloir le supprimer ?",
"Auto": "Auto",
"Dark": "Sombre",
"Light": "Clair"
}
10 changes: 10 additions & 0 deletions src/DotNetEd.CoreAdmin/Translations/it-IT.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"Create": "Creare",
"AccessDatabaseEntries": "Accedi alle voci del database a sinistra.",
"DevelopmentModeMessage": "Sei in modalità Sviluppo. Nessuna sicurezza è stata impostata su Core Admin. L'amministratore principale non sarà accessibile negli ambienti non di sviluppo fino a quando la sicurezza non sarà stata impostata. Vedere la documentazione all'indirizzo https://github.com/edandersen/core-admin.",
"CreateNew": "Crea nuovo",
"GoBack": "Torna indietro",
"Edit": "Modifica",
"Delete": "Cancella",
"DeleteConfirm": "Sei sicuro di voler cancellare questo dato?"
}
11 changes: 11 additions & 0 deletions src/DotNetEd.CoreAdmin/Translations/pt-BR.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"Create": "Criar",
"AccessDatabaseEntries": "Consulte os registros através do painel à esquerda.",
"DevelopmentModeMessage": "Você está no ambiente de desenvolvimento. Nenhuma segurança foi definida para o Core Admin, e para acessá-lo em outros ambientes é necessário realizar as devidas configurações de segurança. Veja a documentação em: https://github.com/edandersen/core-admin.",
"CreateNew": "Novo registro",
"GoBack": "Voltar",
"Edit": "Editar",
"Delete": "Remover",
"DeleteConfirm": "Você tem certeza que deseja remover esse item?"
}

Loading