@namespace OpenArchival.Blazor.AdminPages.Shared @using OpenArchival.Blazor.AdminPages.Shared; @using Microsoft.AspNetCore.Components.Web @using Microsoft.EntityFrameworkCore @using MudBlazor @using MudExtensions @using OpenArchival.Blazor.ArchiveSearch @using OpenArchival.DataAccess @using Microsoft.AspNetCore.Identity @layout AdminControlPanelLayout New User Edit @inject IArtifactGroupingProvider GroupingProvider; @inject IDialogService DialogService; @inject IArtifactGroupingProvider GroupingProvider; @inject IDbContextFactory ContextFactory; @inject ISnackbar Snackbar; @inject NavigationManager NavigationManager; @inject UserManager UserManager; @inject RoleManager RoleManager; @inject IDbContextFactory ContextFactory; @code { public class UserRowElementComparer: IEqualityComparer { public bool Equals(UserDto? x, UserDto? y) { if (ReferenceEquals(x, y)) return true; if (x is null || y is null) return false; return x.Id == y.Id; } public int GetHashCode(UserDto obj) { return obj.Id.GetHashCode(); } } private UserRowElementComparer _comparer = new(); public MudDataGrid DataGrid { get; set; } = default!; private HashSet _selectedItems = new(new UserRowElementComparer()); private string _searchString { get; set; } = ""; protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); StateHasChanged(); } private async Task CreateNewUser(MouseEventArgs args) { var options = new DialogOptions { CloseOnEscapeKey = true, BackdropClick = false}; var dialog = await DialogService.ShowAsync("Create a User", options); var newUserDto = await dialog.Result; if (newUserDto is not null && !newUserDto.Canceled) { var userDto = (UserDto?)newUserDto.Data; if (userDto is null) { Snackbar.Add("The new user dialog did not return a user model.", Severity.Error); return; } if (userDto.Username is null) { Snackbar.Add("The user model returned by the dialog did not have a username set."); return; } if (userDto.Password is null) { Snackbar.Add("The user model returned by the dialog did not have a password.", Severity.Error); return; } var appUser = UserDto.ToApplicationUser(userDto); if (appUser.Email is null) { Snackbar.Add("The application user converted from the DTO did not have an email"); return; } ApplicationUser? existing = await UserManager.FindByEmailAsync(appUser.Email); if (existing is not null) { Snackbar.Add("A user with this email already exists!", Severity.Warning); return; } appUser.EmailConfirmed = true; // Bypass email confirmation IdentityResult result = await UserManager.CreateAsync(appUser, userDto.Password); if (result.Succeeded) { IdentityResult roleResult = await UserManager.AddToRolesAsync(appUser, userDto.Roles); if (!roleResult.Succeeded) { Snackbar.Add("Failed to set roles on user!", Severity.Error); foreach (IdentityError error in roleResult.Errors) { Snackbar.Add(error.Description, Severity.Error); } } Snackbar.Add("User created!", Severity.Success); await DataGrid.ReloadServerData(); StateHasChanged(); return; } else { Snackbar.Add($"Failed to create user with errors, check logs. {result.Errors.ToString()}"); return; } } } private async Task OnEditClicked(UserDto row) { } private async Task OnDeleteClicked(UserDto row) { // Check if we are trying to delete the last Admin user // solution: https://stackoverflow.com/questions/20449008/count-the-number-of-users-in-a-role-in-aspnet-identity-not-membership var context = await ContextFactory.CreateDbContextAsync(); IdentityRole adminRole = await context.Roles.FirstAsync(r => r.Name == UserRoles.Admin); Int32 countAdminUsers = await context.Set().CountAsync(); if (countAdminUsers == 1) { Snackbar.Add("Cannot delete the only admin user!", Severity.Error); return; } bool? confirmed = await DialogService.ShowMessageBox ( new MessageBoxOptions(){ Message=$"Are you sure you want to delete this user?", Title="Delete User?", CancelText="Cancel", YesText="Delete" }); if (confirmed is not null) { // Grab the user from the database, ASP.NET identity will only allow you to // delete the exact object from the database if (row.Id is null) { Snackbar.Add("The user row ID was null, cannot delete", Severity.Error); return; } ApplicationUser? appUser = await UserManager.FindByIdAsync(row.Id); if (appUser is null) { Snackbar.Add("User no longer in database, cannot find by ID.", Severity.Error); return; } IdentityResult result = await UserManager.DeleteAsync(appUser); if (result.Succeeded) { await DataGrid.ReloadServerData(); StateHasChanged(); Snackbar.Add("User deleted!", Severity.Success); } else { Snackbar.Add("Call to DeleteAsync failed.", Severity.Error); foreach (var error in result.Errors) { Snackbar.Add(error.Description, Severity.Error); } } } } private async Task> ServerReload(GridState state) { var context = await ContextFactory.CreateDbContextAsync(); int totalItems = await context.Users.CountAsync(); IEnumerable users = await context.Users .Skip(state.Page * state.PageSize) .Take(state.PageSize) .Select(user=>new UserDto { Id = user.Id, Username = user.UserName, Roles = (from userRole in context.UserRoles join role in context.Roles on userRole.RoleId equals role.Id where userRole.UserId == user.Id select role.Name).ToList() }).ToListAsync(); return new GridData() { TotalItems = totalItems, Items = users }; } public async Task> SelectedItems() { List selectedUsers = []; foreach (UserDto row in DataGrid.SelectedItems) { await using var context = await ContextFactory.CreateDbContextAsync(); var user = await context.Users.Where(user => user.Id == row.Id).FirstAsync(); if (user is null) { Snackbar.Add($"User with id {row.Id} no longer exists in the database.", Severity.Error); continue; } selectedUsers.Add(user); } return selectedUsers; } public async Task SetSelectedItemsAsync(IEnumerable postsToSelect) { var context = await ContextFactory.CreateDbContextAsync(); var rowElementsToSelect = postsToSelect.Select(user=> new UserDto { Id = user.Id, Username = user.UserName, Roles = (from userRole in context.UserRoles join role in context.Roles on userRole.RoleId equals role.Id select role.Name).ToList() }); _selectedItems = new HashSet(rowElementsToSelect, _comparer); _selectedItems = new HashSet(); StateHasChanged(); } public void ClearSelected() { _selectedItems = new HashSet(_comparer); } }