Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs-website/docs/admin-panel/accounts.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ new password, use the account edit page and set a new password there.

## Deleting accounts

Accounts can be deleted from the generic edit page. Consider banning instead —
Use **Delete** in the account list and confirm the prompt. This permanently
removes the account and its dependent characters, items, vault, and other account
data. Disconnect an online account before deleting it. Consider banning instead —
a banned account keeps the character names reserved and keeps the history of what
happened on your server.
1 change: 1 addition & 0 deletions src/Web/AdminPanel/Pages/Accounts.razor
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<td>@item.EMail</td>
<td>
<NavLink href="@($"edit-account/{item.GetId()}/{typeof(Account).FullName}/{item.GetId()}")" class="btn btn-primary"><span class="oi oi-pencil" aria-hidden="true"></span> Edit</NavLink>
<button type="button" class="btn btn-danger ms-1" @onclick="@(() => this._accountService.DeleteAsync(item))">@Resources.Delete</button>
</td>
</ItemTemplate>
<TableFooter>
Expand Down
37 changes: 35 additions & 2 deletions src/Web/Shared/Services/AccountService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace MUnique.OpenMU.Web.Shared.Services;
using System.Diagnostics.CodeAnalysis;
using MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.Persistence;
using MUnique.OpenMU.Web.Shared;
using MUnique.OpenMU.Web.Shared.Components;
using MUnique.OpenMU.Web.Shared.Components.Form.Modal;
using MUnique.OpenMU.Web.Shared.Components.Modal;
Expand All @@ -20,6 +21,7 @@ public class AccountService : IDataService<Account>, ISupportDataChangedNotifica
{
private readonly IDataSource<Account> _dataSource;
private readonly IModalService _modalService;
private readonly IPersistenceContextProvider _contextProvider;
private readonly Debouncer _debouncer = new(300);

private string _searchFilter = string.Empty;
Expand All @@ -29,10 +31,12 @@ public class AccountService : IDataService<Account>, ISupportDataChangedNotifica
/// </summary>
/// <param name="dataSource">The player context.</param>
/// <param name="modalService">The modal service.</param>
public AccountService(IDataSource<Account> dataSource, IModalService modalService)
/// <param name="contextProvider">The persistence context provider.</param>
public AccountService(IDataSource<Account> dataSource, IModalService modalService, IPersistenceContextProvider contextProvider)
{
this._dataSource = dataSource;
this._modalService = modalService;
this._contextProvider = contextProvider;
}

/// <inheritdoc />
Expand Down Expand Up @@ -117,6 +121,35 @@ public async ValueTask UnbanAsync(Account account)
await context.SaveChangesAsync().ConfigureAwait(false);
}

/// <summary>
/// Deletes an account and its dependent data after confirmation.
/// </summary>
/// <param name="account">The account to delete.</param>
/// <returns>Whether the account was deleted.</returns>
public async Task<bool> DeleteAsync(Account account)
{
var confirmed = await this._modalService.ShowQuestionAsync(
"Delete account",
$"Permanently delete account '{account.LoginName}' and all its characters, items, and related data?").ConfigureAwait(false);
if (!confirmed)
{
return false;
}

using var context = this._contextProvider.CreateNewContext();
var storedAccount = await context.GetByIdAsync<Account>(account.GetId()).ConfigureAwait(false);
if (storedAccount is null)
{
return false;
}

// DeleteAsync also removes aggregate members such as the vault; database cascades remove descendants.
await context.DeleteAsync(storedAccount).ConfigureAwait(false);
await context.SaveChangesAsync().ConfigureAwait(false);
this.DataChanged?.Invoke(this, EventArgs.Empty);
return true;
}

/// <summary>
/// Creates a new Account in a modal dialog.
/// </summary>
Expand Down Expand Up @@ -190,4 +223,4 @@ private class AccountCreationParameters
[Display(ResourceType = typeof(Resources), Name = nameof(Resources.AccountCreationParameters_State_Name))]
public AccountState State { get; set; }
}
}
}