-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5e5bbc2
commit b0f1d45
Showing
6 changed files
with
372 additions
and
0 deletions.
There are no files selected for viewing
38 changes: 38 additions & 0 deletions
38
...oring/items/Mvp/Feature.Selections/items/renderings/Selections/Admin/Users/MergeUsers.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
13 changes: 13 additions & 0 deletions
13
headapps/MvpSite/MvpSite.Rendering/Extensions/ICollectionExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
headapps/MvpSite/MvpSite.Rendering/Models/Admin/MergeUsersModel.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | ||
} |
131 changes: 131 additions & 0 deletions
131
headapps/MvpSite/MvpSite.Rendering/ViewComponents/Admin/MergeUsersViewComponent.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
159 changes: 159 additions & 0 deletions
159
headapps/MvpSite/MvpSite.Rendering/Views/Shared/Components/AdminMergeUsers/Default.cshtml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |