Implement the ability for moderators to delete all scores/comments by a user (#1027)

* Implement delete all scores/comments

* Fix formatting in AdminUserController.cs

* Move logging out of loop

* Batch delete scores based on UserId

* Batch update comments instead of using a foreach

* Use html entity instead of apostrophe character

* Confirm before deleting all comments/scores

* Remove unnecessary database.SaveChanges
This commit is contained in:
Kat 2024-06-14 19:27:18 -07:00 committed by GitHub
parent e89a4c27fa
commit be11e138f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 63 additions and 1 deletions

View file

@ -1,7 +1,9 @@
#nullable enable
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Files;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
using LBPUnion.ProjectLighthouse.Types.Logging;
@ -27,7 +29,8 @@ public class AdminUserController : ControllerBase
/// Resets the user's earth decorations to a blank state. Useful for users who abuse audio for example.
/// </summary>
[HttpGet("wipePlanets")]
public async Task<IActionResult> WipePlanets([FromRoute] int id) {
public async Task<IActionResult> WipePlanets([FromRoute] int id)
{
UserEntity? user = this.database.UserFromWebRequest(this.Request);
if (user == null || !user.IsModerator) return this.NotFound();
@ -91,6 +94,53 @@ public class AdminUserController : ControllerBase
return this.Redirect($"/user/{targetedUser.UserId}");
}
/// <summary>
/// Deletes every comment by the user. Useful in case of mass spam
/// </summary>
[HttpGet("wipeComments")]
public async Task<IActionResult> WipeComments([FromRoute] int id)
{
UserEntity? user = this.database.UserFromWebRequest(this.Request);
if (user == null || !user.IsModerator) return this.NotFound();
UserEntity? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
if (targetedUser == null) return this.NotFound();
// Find every comment by the user, then set the deletion info on them
await this.database.Comments.Where(c => c.PosterUserId == targetedUser.UserId)
.ExecuteUpdateAsync(s =>
s.SetProperty(c => c.Deleted, true)
.SetProperty(c => c.DeletedBy, user.Username)
.SetProperty(c => c.DeletedType, "moderator"));
Logger.Success($"Deleted comments for {targetedUser.Username} (id:{targetedUser.UserId})", LogArea.Admin);
await this.database.SendNotification(targetedUser.UserId,
"Your comments have been deleted by a moderator.");
return this.Redirect($"/user/{targetedUser.UserId}");
}
/// <summary>
/// Deletes every score from the user. Useful in the case where a user cheated a ton of scores
/// </summary>
[HttpGet("wipeScores")]
public async Task<IActionResult> WipeScores([FromRoute] int id)
{
UserEntity? user = this.database.UserFromWebRequest(this.Request);
if (user == null || !user.IsModerator) return this.NotFound();
UserEntity? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
if (targetedUser == null) return this.NotFound();
// Find and delete every score uploaded by the target user
await this.database.Scores.Where(c => c.UserId == targetedUser.UserId).ExecuteDeleteAsync();
Logger.Success($"Deleted scores for {targetedUser.Username} (id:{targetedUser.UserId})", LogArea.Admin);
await this.database.SendNotification(targetedUser.UserId, "Your scores have been deleted by a moderator.");
return this.Redirect($"/user/{targetedUser.UserId}");
}
/// <summary>
/// Forces the email verification of a user.

View file

@ -324,6 +324,18 @@ else
<i class="trash alternate icon"></i>
<span>Wipe Earth Decorations</span>
</a>
<a class="ui red button" href="/moderation/user/@Model.ProfileUser.UserId/wipeComments"
onclick="return confirm('Are you sure you want to delete ALL of this user\'s comments? This action cannot be reversed.')">
<i class="trash alternate icon"></i>
<span>Wipe User&apos;s Comments</span>
</a>
<a class="ui red button" href="/moderation/user/@Model.ProfileUser.UserId/wipeScores"
onclick="return confirm('Are you sure you want to delete ALL of this user\'s scores? This action cannot be reversed.')">
<i class="trash alternate icon"></i>
<span>Wipe User&apos;s Scores</span>
</a>
@if (!Model.CommentsDisabledByModerator)
{