more code behind. and more orga power

This commit is contained in:
Holger Börchers 2020-07-30 22:35:22 +02:00
parent 114b52c963
commit 61b32841bd
12 changed files with 231 additions and 91 deletions

View File

@ -1,12 +1,30 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
namespace UserService.DatabaseLayer.DataModels namespace UserService.DatabaseLayer.DataModels
{ {
public class OrganizationUnit : Node public class OrganizationUnit : Node
{ {
public Member? Manager { get; set; } public Member? Manager { get; set; }
/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder();
if (Level != 0)
{
sb.Append("|");
}
sb.Append('-', Level * 4);
return sb + CommonName;
}
} }
public class SecurityGroup : Member public class SecurityGroup : Member
@ -51,10 +69,9 @@ namespace UserService.DatabaseLayer.DataModels
public Node? Parent { get; set; } //Parent public Node? Parent { get; set; } //Parent
public int? ParentId { get; set; } public int? ParentId { get; set; }
public override string ToString() public override string ToString() => $"[{GetType().Name}] {Id:D5} {CommonName}";
{
return $"[{GetType().Name}] {Id:D5} {CommonName}"; public int Level => Parent?.Level + 1 ?? 0;
}
/// <inheritdoc /> /// <inheritdoc />
public virtual object Clone() => MemberwiseClone(); public virtual object Clone() => MemberwiseClone();

View File

@ -12,14 +12,14 @@ namespace UserService.DatabaseLayer.Repository
{ {
public class BaseRepository<T> where T : Node public class BaseRepository<T> where T : Node
{ {
private readonly Func<UserServiceDbContext, DbSet<T>> _context; protected readonly Func<UserServiceDbContext, DbSet<T>> _context;
protected BaseRepository(Func<UserServiceDbContext, DbSet<T>> context) protected BaseRepository(Func<UserServiceDbContext, DbSet<T>> context)
{ {
_context = context; _context = context;
} }
public async Task<IReadOnlyList<T>> GetAllAsync(CancellationToken token = default) public virtual async Task<IReadOnlyList<T>> GetAllAsync(CancellationToken token = default)
{ {
await using var db = new UserServiceDbContext(); await using var db = new UserServiceDbContext();
return await _context(db).Include(x => x.Parent).ToListAsync(token); return await _context(db).Include(x => x.Parent).ToListAsync(token);

View File

@ -1,9 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using UserService.DatabaseLayer.DataModels; using UserService.DatabaseLayer.DataModels;
namespace UserService.DatabaseLayer.Repository namespace UserService.DatabaseLayer.Repository
@ -13,6 +15,36 @@ namespace UserService.DatabaseLayer.Repository
public OrganizationUnitsRepository() : base(x => x.OrganizationUnits) public OrganizationUnitsRepository() : base(x => x.OrganizationUnits)
{ {
} }
/// <inheritdoc />
public override async Task<IReadOnlyList<OrganizationUnit>> GetAllAsync(CancellationToken token = default)
{
await using var db = new UserServiceDbContext();
var result = new List<OrganizationUnit>();
var rootOus = await _context(db)
.Include(x => x.Parent)
.ToListAsync(token);
IEnumerable<OrganizationUnit> Rec(Node node)
{
if (!(node is OrganizationUnit organizationUnit)) yield break;
yield return organizationUnit;
foreach (var ouChild in rootOus.Where(x=>x.ParentId != null && x.ParentId == organizationUnit.Id))
{
foreach (var unit in Rec(ouChild))
{
yield return unit;
}
}
}
foreach (var ou in rootOus.Where(x=> x.ParentId is null))
{
result.AddRange(Rec(ou));
}
return result;
}
} }
public class SecurityGroupsRepository : BaseRepository<SecurityGroup>, ISecurityGroupsRepository public class SecurityGroupsRepository : BaseRepository<SecurityGroup>, ISecurityGroupsRepository

View File

@ -1,27 +0,0 @@
//------------------------------------------------------------------------------
// Generated by the DevExpress.Blazor package.
// To prevent this operation, add the DxExtendStartupHost property to the project and set this property to False.
//
// UserService.Test.csproj:
//
// <Project Sdk="Microsoft.NET.Sdk.Web">
// <PropertyGroup>
// <TargetFramework>netcoreapp3.1</TargetFramework>
// <DxExtendStartupHost>False</DxExtendStartupHost>
// </PropertyGroup>
//------------------------------------------------------------------------------
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
[assembly: HostingStartup(typeof(UserService.Test.DevExpressHostingStartup))]
namespace UserService.Test {
public partial class DevExpressHostingStartup : IHostingStartup {
void IHostingStartup.Configure(IWebHostBuilder builder) {
builder.ConfigureServices((serviceCollection) => {
serviceCollection.AddDevExpressBlazor();
});
}
}
}

Binary file not shown.

View File

@ -0,0 +1,62 @@
@page "/organizationUnits"
@using UserService.DatabaseLayer.DataModels
@inherits OrganizationUnitsBase
<h1>List of all organization units</h1>
@if (OrganizationUnits is null)
{
<p>
<em>Loading...</em>
</p>
}
else
{
<MatTable Items="@OrganizationUnits" class="mat-elevation-z5">
<MatTableHeader>
<th style="width: 20%">Common Name</th>
<th style="width: 20%">Description</th>
<th style="width: 20%">Parent</th>
<th style="width: auto">Manager</th>
<th style="width: auto"> </th>
<th style="width: auto"> </th>
</MatTableHeader>
<MatTableRow>
<td>@context.CommonName</td>
<td>@context.Description</td>
<td>@context.Parent</td>
<td>@context.Manager</td>
<td>
<a href="organizationUnits" @onclick="@(e => Edit(context))">edit</a>
</td>
<td>
<a href="organizationUnits" @onclick="@(e => Delete(context))">delete</a>
</td>
</MatTableRow>
</MatTable>
<MatButton @onclick="@(e => Edit(new OrganizationUnit()))">Create organization unit</MatButton>
}
<MatDialog @bind-IsOpen="@DialogIsOpen">
@if (OuToEdit != null)
{
<MatDialogTitle>@(OuToEdit.Id == 0 ? "New" : "Edit") @OuToEdit.CommonName (@OuToEdit.Id)</MatDialogTitle>
<MatDialogContent>
<MatTextField Label="Common name" @bind-Value="@OuToEdit.CommonName" ReadOnly="@(OuToEdit.Id != 0)"/>
<p/>
<MatTextField Label="Description" @bind-Value="@OuToEdit.Description"/>
@*<MatTextField Label="Manager" @bind-Value="@OuToEdit.Manager"/>*@
<p/>
<MatSelectItem Items="@OrganizationUnits" Label="Parent" @bind-Value="@OuToEdit.Parent"/>
</MatDialogContent>
}
else
{
<MatDialogTitle>No securityGroup selected</MatDialogTitle>
}
<MatDialogActions>
<MatButton OnClick="@(e => { DialogIsOpen = false; })">No Thanks</MatButton>
<MatButton OnClick="@OkClick">OK</MatButton>
</MatDialogActions>
</MatDialog>

View File

@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using UserService.DatabaseLayer.DataModels;
using UserService.DatabaseLayer.Repository;
namespace UserService.Pages
{
public class OrganizationUnitsBase : ComponentBase
{
[Inject] private IOrganizationUnitsRepository OrganizationUnitsRepository { get; set; } = null!;
protected bool DialogIsOpen { get; set; }
protected OrganizationUnit? OuToEdit { get; private set; }
protected IReadOnlyList<OrganizationUnit>? OrganizationUnits { get; private set; }
protected override async Task OnInitializedAsync()
{
OrganizationUnits = await OrganizationUnitsRepository.GetAllAsync().ConfigureAwait(false);
}
protected void Edit(OrganizationUnit organizationUnit)
{
DialogIsOpen = true;
OuToEdit = organizationUnit;
}
protected async Task OkClick()
{
if (OuToEdit is null) return;
await OrganizationUnitsRepository.UpdateAsync(OuToEdit).ConfigureAwait(false);
DialogIsOpen = false;
}
protected async Task Delete(OrganizationUnit organizationUnit)
{
await OrganizationUnitsRepository.DeleteAsync(organizationUnit).ConfigureAwait(false);
}
}
}

View File

@ -1,13 +1,11 @@
@page "/securitygroups" @page "/securitygroups"
@using UserService.DatabaseLayer.DataModels @using UserService.DatabaseLayer.DataModels
@using UserService.DatabaseLayer.Repository @using UserService.DatabaseLayer.Repository
@inject ISecurityGroupsRepository SecurityGroupsRepository @inherits SecurityGroupsBase
@inject IOrganizationUnitsRepository OrganizationUnits
<h1>Table of all security groups</h1> <h1>Table of all security groups</h1>
@if (_securityGroups == null) @if (SecurityGroups is null)
{ {
<p> <p>
<em>Loading...</em> <em>Loading...</em>
@ -15,7 +13,7 @@
} }
else else
{ {
<MatTable Items="@_securityGroups" class="mat-elevation-z5"> <MatTable Items="@SecurityGroups" class="mat-elevation-z5">
<MatTableHeader> <MatTableHeader>
<th style="width: 30%">Common Name</th> <th style="width: 30%">Common Name</th>
<th style="width: 20%">Description</th> <th style="width: 20%">Description</th>
@ -37,17 +35,17 @@ else
<MatButton @onclick="@(e => EditSecurityGroup(new SecurityGroup()))">Create new group</MatButton> <MatButton @onclick="@(e => EditSecurityGroup(new SecurityGroup()))">Create new group</MatButton>
} }
<MatDialog @bind-IsOpen="@_dialogIsOpen"> <MatDialog @bind-IsOpen="@DialogIsOpen">
@if (_securityGroupToEdit != null) @if (SecurityGroupToEdit != null)
{ {
<MatDialogTitle>@(_securityGroupToEdit.Id == 0 ? "New" : "Edit") @_securityGroupToEdit.CommonName (@_securityGroupToEdit.Id)</MatDialogTitle> <MatDialogTitle>@(SecurityGroupToEdit.Id == 0 ? "New" : "Edit") @SecurityGroupToEdit.CommonName (@SecurityGroupToEdit.Id)</MatDialogTitle>
<MatDialogContent> <MatDialogContent>
<MatTextField Label="Common name" @bind-Value="@_securityGroupToEdit.CommonName" ReadOnly="@(_securityGroupToEdit.Id != 0)"></MatTextField> <MatTextField Label="Common name" @bind-Value="@SecurityGroupToEdit.CommonName" ReadOnly="@(SecurityGroupToEdit.Id != 0)"></MatTextField>
<p /> <p />
<MatTextField Label="Description" @bind-Value="@_securityGroupToEdit.Description"></MatTextField> <MatTextField Label="Description" @bind-Value="@SecurityGroupToEdit.Description"></MatTextField>
<MatTextField Label="E-Mail" @bind-Value="@_securityGroupToEdit.EMail"></MatTextField> <MatTextField Label="E-Mail" @bind-Value="@SecurityGroupToEdit.EMail"></MatTextField>
<p /> <p />
<MatAutocompleteList Items="@_organizationUnits" TItem="Node" Label="Parent" CustomStringSelector="@(i => i.CommonName)" @bind-Value="@_securityGroupToEdit.Parent"></MatAutocompleteList> <MatSelectItem Items="@OrganizationUnits" Label="Parent" @bind-Value="@SecurityGroupToEdit.Parent"></MatSelectItem>
</MatDialogContent> </MatDialogContent>
} }
else else
@ -55,41 +53,7 @@ else
<MatDialogTitle>No securityGroup selected</MatDialogTitle> <MatDialogTitle>No securityGroup selected</MatDialogTitle>
} }
<MatDialogActions> <MatDialogActions>
<MatButton OnClick="@(e => { _dialogIsOpen = false; })">No Thanks</MatButton> <MatButton OnClick="@(e => { DialogIsOpen = false; })">No Thanks</MatButton>
<MatButton OnClick="@OkClick">OK</MatButton> <MatButton OnClick="@OkClick">OK</MatButton>
</MatDialogActions> </MatDialogActions>
</MatDialog> </MatDialog>
@code {
bool _dialogIsOpen;
SecurityGroup _securityGroupToEdit;
private IReadOnlyList<SecurityGroup> _securityGroups;
private IReadOnlyList<OrganizationUnit> _organizationUnits;
protected override async Task OnInitializedAsync()
{
_securityGroups = await SecurityGroupsRepository.GetAllAsync();
_organizationUnits = await OrganizationUnits.GetAllAsync().ConfigureAwait(false);
}
private void EditSecurityGroup(SecurityGroup securityGroup)
{
_dialogIsOpen = true;
_securityGroupToEdit = (SecurityGroup)securityGroup.Clone();
}
async Task OkClick()
{
await SecurityGroupsRepository.UpdateAsync(_securityGroupToEdit).ConfigureAwait(false);
await OnInitializedAsync().ConfigureAwait(false);
_dialogIsOpen = false;
}
private async Task DeleteSecurityGroup(SecurityGroup securityGroup)
{
await SecurityGroupsRepository.DeleteAsync(securityGroup).ConfigureAwait(false);
await OnInitializedAsync().ConfigureAwait(false);
}
}

View File

@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using UserService.DatabaseLayer.DataModels;
using UserService.DatabaseLayer.Repository;
namespace UserService.Pages
{
public class SecurityGroupsBase : ComponentBase
{
[Inject] private ISecurityGroupsRepository? SecurityGroupsRepository { get; set; }
[Inject] private IOrganizationUnitsRepository? OrganizationUnitsRepository { get; set; }
protected bool DialogIsOpen;
protected SecurityGroup? SecurityGroupToEdit;
protected IReadOnlyList<SecurityGroup>? SecurityGroups;
protected IReadOnlyList<OrganizationUnit>? OrganizationUnits;
protected override async Task OnInitializedAsync()
{
SecurityGroups = await SecurityGroupsRepository.GetAllAsync().ConfigureAwait(false);
OrganizationUnits = await OrganizationUnitsRepository.GetAllAsync().ConfigureAwait(false);
}
protected void EditSecurityGroup(SecurityGroup securityGroup)
{
SecurityGroupToEdit = securityGroup;
DialogIsOpen = true;
}
protected async Task OkClick()
{
if (!(SecurityGroupToEdit is null))
{
await SecurityGroupsRepository.UpdateAsync(SecurityGroupToEdit).ConfigureAwait(false);
}
DialogIsOpen = false;
}
protected async Task DeleteSecurityGroup(SecurityGroup securityGroup)
{
await SecurityGroupsRepository.DeleteAsync(securityGroup).ConfigureAwait(false);
}
}
}

View File

@ -43,16 +43,16 @@ else
{ {
<MatDialogTitle>@(UserToEdit.Id == 0 ? "New" : "Edit") @UserToEdit.CommonName (@UserToEdit.Id)</MatDialogTitle> <MatDialogTitle>@(UserToEdit.Id == 0 ? "New" : "Edit") @UserToEdit.CommonName (@UserToEdit.Id)</MatDialogTitle>
<MatDialogContent> <MatDialogContent>
<MatTextField Label="Common name" @bind-Value="@UserToEdit.CommonName" ReadOnly="@(UserToEdit.Id != 0)"></MatTextField> <MatTextField Label="Common name" @bind-Value="@UserToEdit.CommonName" ReadOnly="@(UserToEdit.Id != 0)" />
<MatSlideToggle Label="Is active" @bind-Value="@UserToEdit.IsActive"></MatSlideToggle> <MatSlideToggle Label="Is active" @bind-Value="@UserToEdit.IsActive" />
<p /> <p />
<MatTextField Label="First name" @bind-Value="@UserToEdit.FirstName"></MatTextField> <MatTextField Label="First name" @bind-Value="@UserToEdit.FirstName" />
<MatTextField Label="Last name" @bind-Value="@UserToEdit.LastName"></MatTextField> <MatTextField Label="Last name" @bind-Value="@UserToEdit.LastName" />
<p /> <p />
<MatTextField Label="Description" @bind-Value="@UserToEdit.Description"></MatTextField> <MatTextField Label="Description" @bind-Value="@UserToEdit.Description" />
<MatTextField Label="E-Mail" @bind-Value="@UserToEdit.EMail"></MatTextField> <MatTextField Label="E-Mail" @bind-Value="@UserToEdit.EMail" />
<p /> <p />
<MatAutocompleteList Items="@OrganizationUnits" TItem="Node" Label="Parent" CustomStringSelector="@(i => i.CommonName)" @bind-Value="@UserToEdit.Parent"></MatAutocompleteList> <MatSelectItem Items="@OrganizationUnits" TItem="Node" Label="Parent" @bind-Value="@UserToEdit.Parent" />
</MatDialogContent> </MatDialogContent>
} }
else else

View File

@ -33,14 +33,12 @@ namespace UserService.Pages
{ {
if (UserToEdit is null) return; if (UserToEdit is null) return;
await UsersRepository.UpdateAsync(UserToEdit).ConfigureAwait(false); await UsersRepository.UpdateAsync(UserToEdit).ConfigureAwait(false);
await OnInitializedAsync().ConfigureAwait(false);
DialogIsOpen = false; DialogIsOpen = false;
} }
protected async Task DeleteUser(User user) protected async Task DeleteUser(User user)
{ {
await UsersRepository.DeleteAsync(user).ConfigureAwait(false); await UsersRepository.DeleteAsync(user).ConfigureAwait(false);
await OnInitializedAsync().ConfigureAwait(false);
} }
} }
} }

View File

@ -23,6 +23,11 @@
<span class="oi oi-plus" aria-hidden="true"></span> Security groups <span class="oi oi-plus" aria-hidden="true"></span> Security groups
</NavLink> </NavLink>
</li> </li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="organizationUnits">
<span class="oi oi-plus" aria-hidden="true"></span> Organization units
</NavLink>
</li>
<li class="nav-item px-3"> <li class="nav-item px-3">
<NavLink class="nav-link" href="directory"> <NavLink class="nav-link" href="directory">
<span class="oi oi-plus" aria-hidden="true"></span> Directory <span class="oi oi-plus" aria-hidden="true"></span> Directory
@ -40,4 +45,5 @@
{ {
_collapseNavMenu = !_collapseNavMenu; _collapseNavMenu = !_collapseNavMenu;
} }
} }