Skip to content

Commit

Permalink
Added component for merging users
Browse files Browse the repository at this point in the history
  • Loading branch information
IvanLieckens committed Feb 3, 2025
1 parent 5e5bbc2 commit b0f1d45
Show file tree
Hide file tree
Showing 6 changed files with 372 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
ID: "b68ca8f0-65af-4a9a-95be-d9cd757b641b"
Parent: "005295d9-718f-4e20-bcf7-76c37ca95b0e"
Template: "04646a89-996f-4ee7-878a-ffdbf1f0ef0d"
Path: /sitecore/layout/Renderings/Feature/Selections/Admin/Users/MergeUsers
SharedFields:
- ID: "037fe404-dd19-4bf7-8e30-4dadf68b27b0"
Hint: componentName
Value: AdminMergeUsers
- ID: "06d5295c-ed2f-4a54-9bf2-26228d113318"
Hint: __Icon
Value: Office/32x32/users_relation2.png
Languages:
- Language: en
Versions:
- Version: 1
Fields:
- ID: "25bed78c-4957-4165-998a-ca1b52f67497"
Hint: __Created
Value: 20250203T163816Z
- ID: "52807595-0f8f-4b20-8d2a-cb71d28c6103"
Hint: __Owner
Value: |
sitecore\ivan.lieckens@sitecore.com
- ID: "5dd74568-4d4b-44c1-b513-0af5f4cda34f"
Hint: __Created by
Value: |
sitecore\ivan.lieckens@sitecore.com
- ID: "8cdc337e-a112-42fb-bbb4-4143751e123f"
Hint: __Revision
Value: "c26fd65d-504a-4833-9306-06c5918c5047"
- ID: "badd9cf9-53e0-4d0c-bcc0-2d784c282f6a"
Hint: __Updated by
Value: |
sitecore\ivan.lieckens@sitecore.com
- ID: "d9cf14b1-fa16-4ba6-9288-e8a174d4d522"
Hint: __Updated
Value: 20250203T170749Z
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace MvpSite.Rendering.Extensions;

// ReSharper disable once InconsistentNaming - This class extends the interface, not the type
public static class ICollectionExtensions
{
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
{
foreach (T item in items)
{
collection.Add(item);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public static RenderingEngineOptions AddFeatureSelections(this RenderingEngineOp
options.AddViewComponent(ApplicationReviewSettingsViewComponent.ViewComponentName);
options.AddViewComponent(ContributionOverviewViewComponent.ViewComponentName);
options.AddViewComponent(SelectionOverviewViewComponent.ViewComponentName);
options.AddViewComponent(MergeUsersViewComponent.ViewComponentName);
return options;
}
}
30 changes: 30 additions & 0 deletions headapps/MvpSite/MvpSite.Rendering/Models/Admin/MergeUsersModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Mvp.Selections.Domain;

namespace MvpSite.Rendering.Models.Admin;

public class MergeUsersModel : BaseModel
{
public List<User> OldUserOptions { get; init; } = [];

public List<User> TargetUserOptions { get; init; } = [];

public Guid? SelectedOldUserId { get; set; }

public Guid? SelectedTargetUserId { get; set; }

public User? OldUser { get; set; }

public User? TargetUser { get; set; }

public string? OldUserNameSearch { get; set; }

public string? OldUserEmailSearch { get; set; }

public string? TargetUserNameSearch { get; set; }

public string? TargetUserEmailSearch { get; set; }

public bool IsMerging { get; set; }

public User? MergedUser { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Mvp.Selections.Client;
using Mvp.Selections.Client.Models;
using Mvp.Selections.Domain;
using MvpSite.Rendering.Extensions;
using MvpSite.Rendering.Models.Admin;
using Sitecore.AspNetCore.SDK.RenderingEngine.Binding;

namespace MvpSite.Rendering.ViewComponents.Admin;

[ViewComponent(Name = ViewComponentName)]
public class MergeUsersViewComponent(IViewModelBinder modelBinder, MvpSelectionsApiClient client)
: BaseViewComponent(modelBinder, client)
{
public const string ViewComponentName = "AdminMergeUsers";

public override async Task<IViewComponentResult> InvokeAsync()
{
MergeUsersModel model = await ModelBinder.Bind<MergeUsersModel>(ViewContext);
Response<User> userResponse = await Client.GetCurrentUserAsync();
if (userResponse is { StatusCode: HttpStatusCode.OK, Result: not null })
{
User currentUser = userResponse.Result;
if (currentUser.HasRight(Right.Admin))
{
if (!model.IsMerging && (!string.IsNullOrWhiteSpace(model.OldUserNameSearch) || !string.IsNullOrWhiteSpace(model.OldUserEmailSearch)))
{
model.OldUserOptions.AddRange(
await SearchUsersAsync(model, model.OldUserNameSearch, model.OldUserEmailSearch));
}

if (!model.IsMerging && (!string.IsNullOrWhiteSpace(model.TargetUserNameSearch) || !string.IsNullOrWhiteSpace(model.TargetUserEmailSearch)))
{
model.TargetUserOptions.AddRange(
await SearchUsersAsync(model, model.TargetUserNameSearch, model.TargetUserEmailSearch));
}

if (model.SelectedOldUserId.HasValue)
{
model.OldUser = await GetUserAsync(model, model.SelectedOldUserId.Value);
}

if (model.SelectedTargetUserId.HasValue)
{
model.TargetUser = await GetUserAsync(model, model.SelectedTargetUserId.Value);
}

if (model.IsMerging && model is { SelectedOldUserId: not null, SelectedTargetUserId: not null })
{
Response<User> mergedUserResponse = await Client.MergeUsersAsync(model.SelectedOldUserId.Value, model.SelectedTargetUserId.Value);
if (mergedUserResponse is { StatusCode: HttpStatusCode.OK, Result: not null })
{
model.MergedUser = mergedUserResponse.Result;
ModelState.Clear();
model.SelectedOldUserId = null;
model.SelectedTargetUserId = null;
model.OldUserNameSearch = null;
model.OldUserEmailSearch = null;
model.TargetUserNameSearch = null;
model.TargetUserEmailSearch = null;
await LoadApplications(model, model.MergedUser.Id, model.MergedUser);
}
else
{
model.ErrorMessages.Add(mergedUserResponse.Message);
}
}
}
else
{
model.ErrorMessages.Add("Only an Administrator can merge users.");
}
}
else
{
model.ErrorMessages.Add(userResponse.Message);
}

return model.ErrorMessages.Count > 0 ? View("~/Views/Shared/_Error.cshtml", model) : View(model);
}

private async Task<List<User>> SearchUsersAsync(MergeUsersModel model, string? name, string? email)
{
List<User> result = [];
if (!string.IsNullOrWhiteSpace(name) || !string.IsNullOrWhiteSpace(email))
{
Response<IList<User>> response = await Client.GetUsersAsync(name, email);
if (response is { StatusCode: HttpStatusCode.OK, Result: not null })
{
result.AddRange(response.Result);
}
else
{
model.ErrorMessages.Add(response.Message);
}
}

return result;
}

private async Task<User?> GetUserAsync(MergeUsersModel model, Guid userId)
{
User? result = null;
Response<User> response = await Client.GetUserAsync(userId);
if (response is { StatusCode: HttpStatusCode.OK, Result: not null })
{
result = response.Result;
await LoadApplications(model, userId, result);
}
else
{
model.ErrorMessages.Add(response.Message);
}

return result;
}

private async Task LoadApplications(MergeUsersModel model, Guid userId, User user)
{
Response<IList<Application>> response = await Client.GetApplicationsForUserAsync(userId);
if (response is { StatusCode: HttpStatusCode.OK, Result: not null })
{
user.Applications.AddRange(response.Result);
}
else
{
model.ErrorMessages.Add(response.Message);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
@using Mvp.Selections.Domain
@using Mvp.Selections.Domain.Roles
@model MvpSite.Rendering.Models.Admin.MergeUsersModel

<div class="mvp-fs-adminmergeusers col-12 bg-white">
<form method="post">
<div class="row">
<div class="col-6">
<h2>Old User</h2>
<div class="form-row">
<div class="col">
<input asp-for="OldUserNameSearch" class="form-control" placeholder="Name" />
</div>
<div class="col">
<input asp-for="OldUserEmailSearch" class="form-control" placeholder="Email" />
</div>
<div class="col">
<button type="submit" class="btn btn-secondary">Search</button>
</div>
</div>
@if (Model.OldUserOptions.Count > 0)
{
<div class="form-row">
<div class="col">
<select asp-for="SelectedOldUserId" class="form-control">
@foreach (User oldUser in Model.OldUserOptions)
{
<option value="@oldUser.Id">@oldUser.Name (@oldUser.Email)</option>
}
</select>
</div>
<div class="col">
<button type="submit" class="btn btn-secondary">Select</button>
</div>
</div>
}
@if (Model.OldUser != null)
{
<div class="card">
<div class="card-body">
<h3 class="card-title">@Model.OldUser.Name</h3>
<h4 class="card-subtitle">@Model.OldUser.Email</h4>
<p class="card-text">
<ul class="list-group">
<li class="list-group-item">
Roles:
@foreach (Role role in Model.OldUser.Roles)
{
<span class="badge-primary badge-info">@role.Name</span>
}
</li>
<li class="list-group-item">
Applications:
@foreach (Application application in Model.OldUser.Applications)
{
<span class="badge-primary badge-info">@application.Selection.Year</span>
}
</li>
</ul>
</p>
</div>
</div>
}
</div>
<div class="col-6">
<h2>Target User</h2>
<div class="form-row">
<div class="col">
<input asp-for="TargetUserNameSearch" class="form-control" placeholder="Name" />
</div>
<div class="col">
<input asp-for="TargetUserEmailSearch" class="form-control" placeholder="Email" />
</div>
<div class="col">
<button type="submit" class="btn btn-secondary">Search</button>
</div>
</div>
@if (Model.TargetUserOptions.Count > 0)
{
<div class="form-row">
<div class="col">
<select asp-for="SelectedTargetUserId" class="form-control">
@foreach (User targetUser in Model.TargetUserOptions)
{
<option value="@targetUser.Id">@targetUser.Name (@targetUser.Email)</option>
}
</select>
</div>
<div class="col">
<button type="submit" class="btn btn-secondary">Select</button>
</div>
</div>
}
@if (Model.TargetUser != null)
{
<div class="card">
<div class="card-body">
<h3 class="card-title">@Model.TargetUser.Name</h3>
<h4 class="card-subtitle">@Model.TargetUser.Email</h4>
<p class="card-text">
<ul class="list-group">
<li class="list-group-item">
Roles:
@foreach (Role role in Model.TargetUser.Roles)
{
<span class="badge-primary badge-info">@role.Name</span>
}
</li>
<li class="list-group-item">
Applications:
@foreach (Application application in Model.TargetUser.Applications)
{
<span class="badge-primary badge-info">@application.Selection.Year</span>
}
</li>
</ul>
</p>
</div>
</div>
}
</div>
</div>
@if (Model is { OldUser: not null, TargetUser: not null, MergedUser: null })
{
<input asp-for="IsMerging" type="hidden" value="True"/>
<button type="submit" class="btn btn-primary w-100">Merge</button>
}
else if (Model.MergedUser != null)
{
<div class="alert alert-success" role="alert">
Merged!
</div>
<div class="card">
<div class="card-body">
<h3 class="card-title">@Model.MergedUser.Name</h3>
<h4 class="card-subtitle">@Model.MergedUser.Email</h4>
<p class="card-text">
<ul class="list-group">
<li class="list-group-item">
Roles:
@foreach (Role role in Model.MergedUser.Roles)
{
<span class="badge-primary badge-info">@role.Name</span>
}
</li>
<li class="list-group-item">
Applications:
@foreach (Application application in Model.MergedUser.Applications)
{
<span class="badge-primary badge-info">@application.Selection.Year</span>
}
</li>
</ul>
</p>
</div>
</div>
}
</form>
</div>

0 comments on commit b0f1d45

Please sign in to comment.