diff --git a/src/Controllers/GroupsController.cs b/src/Controllers/GroupsController.cs index 08b37e0..b3c7d92 100644 --- a/src/Controllers/GroupsController.cs +++ b/src/Controllers/GroupsController.cs @@ -18,55 +18,86 @@ public class GroupsController : Controller _logger = logger; } - [HttpGet("Index")] - public async Task> Index(GroupsIndexRequestModel requestModel) + [HttpGet("Get")] + public async Task GetAsync(GroupsIndexRequestModel model) { - string? cn = requestModel.Cn; - List attributes = [.. _ldap.GroupsAttributes]; - if (!requestModel.GidNumber) attributes.Remove("gidNumber"); - if (!requestModel.Permissions) attributes.Remove("description"); - IEnumerable groups; - if (cn is null) + if (model is null) { - groups = await _ldap.ListGroupsAsync([.. attributes]); + return new GroupsGetResponseModel( + successful: false, + groupModels: null, + exception: "Unable to create a group because the GroupsCreateRequestModel is null."); } - else + try { - try + + string? cn = model.Cn; + List attributes = [.. _ldap.GroupsAttributes]; + if (!model.GidNumber) attributes.Remove("gidNumber"); + if (!model.Permissions) attributes.Remove("description"); + IEnumerable groups; + if (cn is null) { - groups = [await _ldap.GetGroupByCnAsync(cn, [.. attributes])]; + groups = await _ldap.ListGroupsAsync([.. attributes]); } - catch (InvalidOperationException) + else { - groups = []; + try + { + groups = [await _ldap.GetGroupByCnAsync(cn, [.. attributes])]; + } + catch (InvalidOperationException) + { + groups = []; + } } + return new(true, groups); + } catch (Exception ex) + { + if (model.Cn is not null) + { + _logger.LogError("Unable to get group {model.Cn}: {ex.Message} - {ex.StackTrace}", [model.Cn, ex.Message, ex.StackTrace]); + } + else + { + _logger.LogError("Unable to get groups: {ex.Message} - {ex.StackTrace}", [ex.Message, ex.StackTrace]); + } + return new(false, null, ex.Message); } - return groups; } - [HttpGet("Delete")] - public async Task Delete(string uid) + [HttpDelete("Delete")] + public async Task Delete(string uid) { return await Task.Run(async () => { try { await _ldap.DeleteGroupAsync(uid); - return true; + return new GroupsDeleteResponseModel(true); } catch (Exception) { - return false; + return new GroupsDeleteResponseModel(false); } }); } - [HttpGet("Create")] - public async Task Create(string cn, string gidNumber, GroupPermission[] permissions, string description) - { + [HttpPost("Create")] + public async Task Create([FromBody]GroupsCreateRequestModel model) + { + if (model is null) + { + return new GroupsCreateResponseModel( + successful: false, + exception: "Unable to create a group because the GroupsCreateRequestModel is null."); + } try { - description ??= JsonSerializer.Serialize(new GroupPermissions() {Permissions = []}); + List permissions = model.Permissions; + string gidNumber = "0"; // TODO implement counter + string cn = model.Cn; + string displayName = model.DisplayName; LdapAttributeSet attributeSet = [ @@ -76,29 +107,26 @@ public class GroupsController : Controller new LdapAttribute("gidNumber", gidNumber), new LdapAttribute( "description", - JsonSerializer.Serialize(new GroupPermissions() - { - Permissions = [.. permissions] - })) + JsonSerializer.Serialize(new GroupDescription(){DisplayName = displayName, Permissions = permissions})) ]; await _ldap.CreateGroup(cn, attributeSet); - return true; + return new(true); } catch (Exception ex) { - _logger.LogError("Unable to create user: {ex.Message} - {ex.StackTrace}", [ex.Message, ex.StackTrace]); - return false; + _logger.LogError("Unable to create group: {ex.Message} - {ex.StackTrace}", [ex.Message, ex.StackTrace]); + return new(false, ex.Message); } } - [HttpPost("Update")] - public async Task Update([FromBody]GroupsModifyRequestModel requestModel) + [HttpPatch("Update")] + public async Task Update([FromBody]GroupsModifyRequestModel requestModel) { if (requestModel is null) { _logger.LogError("Unable to update a group because the GroupsModifyRequestModel is null"); - return false; + return new(false, "Unable to update a group because the GroupsModifyRequestModel is null"); } string cn = requestModel.Cn; @@ -111,10 +139,10 @@ public class GroupsController : Controller { await _ldap.UpdateGroup(cn, "gidNumber", requestModel.GidNumber); } - if (requestModel.Permissions is not null) + if (requestModel.Description is not null) { - await _ldap.UpdateGroup(cn, "description", JsonSerializer.Serialize(requestModel.Permissions)); + await _ldap.UpdateGroup(cn, "description", JsonSerializer.Serialize(requestModel.Description)); } - return true; + return new(true); } } \ No newline at end of file diff --git a/src/Controllers/HomeController.cs b/src/Controllers/HomeController.cs index dd5be8f..6b0647f 100644 --- a/src/Controllers/HomeController.cs +++ b/src/Controllers/HomeController.cs @@ -88,7 +88,15 @@ public class HomeController : Controller Workplace = user.Description?.Workplace ?? "" }); } - return View(new UsersIndexViewModel() { UserTableViewModels = UserTableViewModels }); } + return View(new UsersIndexViewModel() { UserTableViewModels = UserTableViewModels }); + } + + [HttpGet("Groups")] + public async Task GroupsAsync() + { + IEnumerable groups = await _ldap.ListGroupsAsync(); + return View(new GroupsIndexViewModel(groups)); + } [HttpPost("Login")] public async Task Login(string username, string password) diff --git a/src/Models/GroupModel.cs b/src/Models/GroupModel.cs index c625c15..0b7c424 100644 --- a/src/Models/GroupModel.cs +++ b/src/Models/GroupModel.cs @@ -6,8 +6,13 @@ namespace Berufsschule_HAM.Models; public class GroupModel { + [JsonPropertyName("Cn")] public required string Cn { get; set; } + [JsonPropertyName("DisplayName")] + public string DisplayName { get; set; } + [JsonPropertyName("GidNumber")] public string? GidNumber { get; set; } + [JsonPropertyName("Permissions")] public List Permissions { get; set; } public GroupModel(Dictionary ldapData) { @@ -20,13 +25,18 @@ public class GroupModel } else { - Permissions = JsonSerializer.Deserialize(descriptionValue)?.Permissions ?? []; + GroupDescription? description = JsonSerializer.Deserialize(descriptionValue); + DisplayName = description?.DisplayName ?? Cn; + Permissions = description?.Permissions ?? []; } } } -public class GroupPermissions +public class GroupDescription { + [JsonPropertyName("DisplayName")] + public required string DisplayName { get; set; } + [JsonPropertyName("Permissions")] public required List Permissions { get; set; } } diff --git a/src/Models/GroupsRequestModels.cs b/src/Models/GroupsRequestModels.cs index 38a78f5..6ae237e 100644 --- a/src/Models/GroupsRequestModels.cs +++ b/src/Models/GroupsRequestModels.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + namespace Berufsschule_HAM.Models; public class GroupsIndexRequestModel @@ -7,10 +9,24 @@ public class GroupsIndexRequestModel public bool Permissions { get; set; } = true; } +public class GroupsCreateRequestModel +{ + [JsonPropertyName("Cn")] + public required string Cn { get; set; } + [JsonPropertyName("DisplayName")] + public required string DisplayName { get; set; } + [JsonPropertyName("Permissions")] + public required List Permissions { get; set; } +} + public class GroupsModifyRequestModel { + [JsonPropertyName("Cn")] public required string Cn { get; set; } + [JsonPropertyName("NewCn")] public string? NewCn { get; set; } = null; + [JsonPropertyName("GidNumber")] public string? GidNumber { get; set; } = null; - public GroupPermissions? Permissions { get; set; } = null; + [JsonPropertyName("Description")] + public GroupDescription? Description { get; set; } = null; } \ No newline at end of file diff --git a/src/Models/GroupsResponseModels.cs b/src/Models/GroupsResponseModels.cs new file mode 100644 index 0000000..ce3fac7 --- /dev/null +++ b/src/Models/GroupsResponseModels.cs @@ -0,0 +1,29 @@ +using System.Text.Json.Serialization; + +namespace Berufsschule_HAM.Models; + +public class GroupsGetResponseModel(bool successful, IEnumerable? groupModels, string exception = "None") +{ + public bool Success { get; set; } = successful; + [JsonPropertyName("GroupModels")] + public IEnumerable GroupModels { get; set; } = groupModels ?? []; + public string? Exception { get; set; } = exception; +} + +public class GroupsCreateResponseModel(bool successful, string exception = "None") +{ + public bool Success { get; set; } = successful; + public string? Exception { get; set; } = exception; +} + +public class GroupsUpdateResponseModel(bool successful, string exception = "None") +{ + public bool Success { get; set; } = successful; + public string? Exception { get; set; } = exception; +} + +public class GroupsDeleteResponseModel(bool successful, string exception = "None") +{ + public bool Success { get; set; } = successful; + public string? Exception { get; set; } = exception; +} \ No newline at end of file diff --git a/src/Models/GroupsViewModels.cs b/src/Models/GroupsViewModels.cs new file mode 100644 index 0000000..362145d --- /dev/null +++ b/src/Models/GroupsViewModels.cs @@ -0,0 +1,38 @@ +namespace Berufsschule_HAM.Models; + +public class GroupsIndexViewModel +{ + public List GroupsTableViewModels { get; set; } = []; + + public GroupsIndexViewModel(IEnumerable groupModels) + { + foreach (GroupModel model in groupModels) + { + GroupsTableViewModels.Add(new() + { + Cn = model.Cn, + Group = model.DisplayName, + CanInventorize = model.Permissions.Any(x => x == GroupPermission.CanInventorize), + CanManageAssets = model.Permissions.Any(x => x == GroupPermission.CanManageAssets), + CanManageGroups = model.Permissions.Any(x => x == GroupPermission.CanManageGroups), + CanManageLocations = model.Permissions.Any(x => x == GroupPermission.CanManageLocations), + CanManageUsers = model.Permissions.Any(x => x == GroupPermission.CanManageUsers) + }); + } + } + + + + +} + +public class GroupsTableViewModel +{ + public required string Cn { get; set; } + public required string Group { get; set; } + public required bool CanInventorize { get; set; } + public required bool CanManageUsers { get; set; } + public required bool CanManageLocations { get; set; } + public required bool CanManageAssets { get; set; } + public required bool CanManageGroups { get; set; } +} \ No newline at end of file diff --git a/src/Resources/Views.Shared._Layout.de.resx b/src/Resources/Views.Shared._Layout.de.resx index d7c5ebc..31369ef 100644 --- a/src/Resources/Views.Shared._Layout.de.resx +++ b/src/Resources/Views.Shared._Layout.de.resx @@ -16,8 +16,8 @@ Zum Inhalt springen - - Startseite + + Schnellaktionen Inventur diff --git a/src/Services/LdapService.cs b/src/Services/LdapService.cs index 65e37e3..2ff006d 100644 --- a/src/Services/LdapService.cs +++ b/src/Services/LdapService.cs @@ -134,6 +134,11 @@ public partial class LdapService : IDisposable return returnValue; } + public async Task> ListGroupsAsync() + { + return await ListGroupsAsync(GroupsAttributes); + } + public async Task> ListGroupsAsync(string[] attributes) { List returnValue = []; diff --git a/src/Views/Home/Assets.cshtml b/src/Views/Home/Assets.cshtml index b052663..8dfc3ca 100644 --- a/src/Views/Home/Assets.cshtml +++ b/src/Views/Home/Assets.cshtml @@ -484,10 +484,6 @@ document.addEventListener('DOMContentLoaded', () => { const input = updateForm.querySelector(`[name="${key}"]`); if (input) input.value = value; } - console.log("responseJson:"); - console.log(responseJson); - console.log("asset:"); - console.log(asset); // Handle nested description fields if (asset.Description) { @@ -529,12 +525,7 @@ document.addEventListener('DOMContentLoaded', () => { const formData = new FormData(updateForm); const jsonData = {}; - console.log("DEBUG@1"); - console.log(jsonData); for (const [key, value] of formData.entries()) { - console.log("DEBUG@1.1"); - console.log(key); - console.log(value); if (!value) continue; const keys = key.split('.'); let target = jsonData; @@ -544,8 +535,6 @@ document.addEventListener('DOMContentLoaded', () => { } target[keys[keys.length - 1]] = value; } - console.log("DEBUG@2"); - console.log(jsonData); const attributes = {}; document.querySelectorAll('#updateAttributesContainer .attribute-row').forEach(row => { diff --git a/src/Views/Home/Groups.cshtml b/src/Views/Home/Groups.cshtml new file mode 100644 index 0000000..aa80e9c --- /dev/null +++ b/src/Views/Home/Groups.cshtml @@ -0,0 +1,488 @@ +@using Microsoft.AspNetCore.Mvc.Localization +@using Berufsschule_HAM.Models +@using System.Buffers.Text +@model GroupsIndexViewModel +@inject IViewLocalizer T +@{ + ViewData["Title"] = T["Groups"]; +} + + +
+

@T["Groups"]

+ + +
+ +
+ + +
+ + + + + + + + + + + + + + @{ + foreach (GroupsTableViewModel groupTableViewModel in Model.GroupsTableViewModels) + { + + + + + + + + + + } + } + +
Group@T["Can"]:
@T["inventorize"]
@T["Can"]:
@T["manage users"]
@T["Can"]:
@T["manage locations"]
@T["Can"]:
@T["manage assets"]
@T["Can"]:
@T["manage groups"]
@T["Action"]
@groupTableViewModel.Group@(groupTableViewModel.CanInventorize ? "☑️" : "❌")@(groupTableViewModel.CanManageUsers ? "☑️" : "❌")@(groupTableViewModel.CanManageLocations ? "☑️" : "❌")@(groupTableViewModel.CanManageAssets ? "☑️" : "❌")@(groupTableViewModel.CanManageGroups ? "☑️" : "❌") +
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Views/Shared/_Layout.cshtml b/src/Views/Shared/_Layout.cshtml index 6ca4965..20fc558 100644 --- a/src/Views/Shared/_Layout.cshtml +++ b/src/Views/Shared/_Layout.cshtml @@ -30,7 +30,7 @@