From 3fcfaaf5cc782e2b3e64c165abb7b9c122a21e2e Mon Sep 17 00:00:00 2001 From: Zaprit Date: Sat, 11 Feb 2023 08:25:06 +0000 Subject: [PATCH] Profile Blocking (#662) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added blocked user DB object * Added user blocking functions * Fixed DB Migration * Updated DB Functions * Added blocked user support to website * Fixed DB Migration * I forgot to save 🫠 * More migration pain * Fixed Unblock label * Update ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml sounds cool Co-authored-by: koko <68549366+sudokoko@users.noreply.github.com> * Removed unnecessary imports in database * Removed unnecessary imports in UserPage.cshtml.cs * Made comments in-game respect blocked users * Update ProjectLighthouse/Database.cs Co-authored-by: Josh * Update ProjectLighthouse/Database.cs Co-authored-by: Josh * DB Code cleanup * Cleaned up userPage block detection code * Get only the creator id in lieu of the whole object * Fixed null condition when not logged in * Fixed null condition when not logged in * Potential DB Optimisation * Apply suggestions from code review Co-authored-by: Josh * Fix errors and null warning * Use explicit type in lieu of var * changed block icons * Optimize blocked user check and save changes when unblocking --------- Co-authored-by: koko <68549366+sudokoko@users.noreply.github.com> Co-authored-by: Josh --- .../Controllers/CommentController.cs | 12 +++- .../Controllers/UserPageController.cs | 28 +++++++++ .../Pages/UserPage.cshtml | 14 +++++ .../Pages/UserPage.cshtml.cs | 11 ++++ ProjectLighthouse/Database.cs | 48 ++++++++++++-- .../20230208165011_AddedUserBlocking.cs | 62 +++++++++++++++++++ .../PlayerData/Profiles/BlockedProfile.cs | 20 ++++++ .../Migrations/DatabaseModelSnapshot.cs | 40 ++++++++++++ 8 files changed, 227 insertions(+), 8 deletions(-) create mode 100644 ProjectLighthouse/Migrations/20230208165011_AddedUserBlocking.cs create mode 100644 ProjectLighthouse/PlayerData/Profiles/BlockedProfile.cs diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs index ba9ac979..c7bd5e52 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs @@ -71,9 +71,15 @@ public class CommentController : ControllerBase if (targetId == 0) return this.NotFound(); - List comments = await this.database.Comments.Include - (c => c.Poster) - .Where(c => c.TargetId == targetId && c.Type == type) + List blockedUsers = await ( + from blockedProfile in this.database.BlockedProfiles + where blockedProfile.UserId == token.UserId + select blockedProfile.BlockedUserId + ).ToListAsync(); + + List comments = await this.database.Comments.Include(c => c.Poster) + .Where(c => c.TargetId == targetId && c.Type == type && !c.Poster.IsBanned) + .Where(c => !blockedUsers.Contains(c.PosterUserId)) .OrderByDescending(c => c.Timestamp) .Skip(Math.Max(0, pageStart - 1)) .Take(Math.Min(pageSize, 30)) diff --git a/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs index e7e1cfb1..1d361f13 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs @@ -87,4 +87,32 @@ public class UserPageController : ControllerBase return this.Redirect("~/user/" + id); } + + [HttpGet("block")] + public async Task BlockUser([FromRoute] int id) + { + WebToken? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); + + User? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + if (blockedUser == null) return this.NotFound(); + + await this.database.BlockUser(token.UserId, blockedUser); + + return this.Redirect("~/user/" + id); + } + + [HttpGet("unblock")] + public async Task UnblockUser([FromRoute] int id) + { + WebToken? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); + + User? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id); + if (blockedUser == null) return this.NotFound(); + + await this.database.UnblockUser(token.UserId, blockedUser); + + return this.Redirect("~/user/" + id); + } } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml index 6dfffc9f..444ce55a 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml @@ -61,6 +61,20 @@
@if (Model.ProfileUser != Model.User && Model.User != null) { + if (!Model.IsProfileUserBlocked) + { + + + Block + + } + else + { + + + Unblock + + } if (!Model.IsProfileUserHearted) { diff --git a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs index 1e32515e..555ffda1 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs @@ -18,6 +18,8 @@ public class UserPage : BaseLayout public bool IsProfileUserHearted; + public bool IsProfileUserBlocked; + public List? Photos; public List? Slots; @@ -86,11 +88,18 @@ public class UserPage : BaseLayout } this.CommentsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled && this.ProfileUser.CommentsEnabled; + if (this.CommentsEnabled) { + List blockedUsers = this.User == null ? new List() : await + (from blockedProfile in this.Database.BlockedProfiles + where blockedProfile.UserId == this.User.UserId + select blockedProfile.BlockedUserId).ToListAsync(); + this.Comments = await this.Database.Comments.Include(p => p.Poster) .OrderByDescending(p => p.Timestamp) .Where(p => p.TargetId == userId && p.Type == CommentType.Profile) + .Where(p => !blockedUsers.Contains(p.PosterUserId)) .Take(50) .ToListAsync(); } @@ -114,6 +123,8 @@ public class UserPage : BaseLayout .Where(h => h.UserId == this.User.UserId) .AnyAsync(); + this.IsProfileUserBlocked = await this.Database.IsUserBlockedBy(this.ProfileUser.UserId, this.User.UserId); + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs index 42c51a93..1bdb0428 100644 --- a/ProjectLighthouse/Database.cs +++ b/ProjectLighthouse/Database.cs @@ -53,7 +53,8 @@ public class Database : DbContext public DbSet APIKeys { get; set; } public DbSet Playlists { get; set; } public DbSet PlatformLinkAttempts { get; set; } - + public DbSet BlockedProfiles { get; set; } + protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseMySql(ServerConfiguration.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion); @@ -131,9 +132,10 @@ public class Database : DbContext Comment? comment = await this.Comments.FirstOrDefaultAsync(c => commentId == c.CommentId); if (comment == null) return false; - if (comment.PosterUserId == userId) return false; + if (await this.IsUserBlockedBy(userId, comment.PosterUserId)) return false; + Reaction? reaction = await this.Reactions.FirstOrDefaultAsync(r => r.UserId == userId && r.TargetId == commentId); if (reaction == null) { @@ -189,14 +191,17 @@ public class Database : DbContext .Select(u => u.UserId) .FirstOrDefaultAsync(); if (targetUserId == 0) return false; + if (await this.IsUserBlockedBy(userId, targetUserId)) return false; } else { - int targetSlotId = await this.Slots.Where(s => s.SlotId == targetId) + int creatorId = await this.Slots.Where(s => s.SlotId == targetId) .Where(s => s.CommentsEnabled && !s.Hidden) - .Select(s => s.SlotId) + .Select(s => s.CreatorId) .FirstOrDefaultAsync(); - if (targetSlotId == 0) return false; + if (creatorId == 0) return false; + + if (await this.IsUserBlockedBy(userId, creatorId)) return false; } this.Comments.Add @@ -312,6 +317,39 @@ public class Database : DbContext await this.SaveChangesAsync(); } + public async Task BlockUser(int userId, User blockedUser) + { + if (userId == blockedUser.UserId) return; + + User? user = await this.Users.FindAsync(userId); + + BlockedProfile blockedProfile = new() + { + User = user, + BlockedUser = blockedUser, + }; + + await this.BlockedProfiles.AddAsync(blockedProfile); + + await this.SaveChangesAsync(); + } + + public async Task UnblockUser(int userId, User blockedUser) + { + if (userId == blockedUser.UserId) return; + + this.BlockedProfiles.RemoveWhere(bp => bp.BlockedUserId == blockedUser.UserId && bp.UserId == userId); + + await this.SaveChangesAsync(); + } + + public async Task IsUserBlockedBy(int userId, int targetId) + { + if (targetId == userId) return false; + + return await this.BlockedProfiles.Has(bp => bp.BlockedUserId == userId && bp.UserId == targetId); + } + #endregion #region User Helper Methods diff --git a/ProjectLighthouse/Migrations/20230208165011_AddedUserBlocking.cs b/ProjectLighthouse/Migrations/20230208165011_AddedUserBlocking.cs new file mode 100644 index 00000000..bf942046 --- /dev/null +++ b/ProjectLighthouse/Migrations/20230208165011_AddedUserBlocking.cs @@ -0,0 +1,62 @@ +using LBPUnion.ProjectLighthouse; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + + [DbContext(typeof(Database))] + [Migration("20230208165011_AddedUserBlocking")] + public partial class AddedUserBlocking : Migration + { + + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "BlockedProfiles", + columns: table => new + { + BlockedProfileId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "int", nullable: false), + BlockedUserId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_BlockedProfiles", x => x.BlockedProfileId); + table.ForeignKey( + name: "FK_BlockedProfiles_Users_BlockedUserId", + column: x => x.BlockedUserId, + principalTable: "Users", + principalColumn: "UserId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_BlockedProfiles_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "UserId", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_BlockedProfiles_BlockedUserId", + table: "BlockedProfiles", + column: "BlockedUserId"); + + migrationBuilder.CreateIndex( + name: "IX_BlockedProfiles_UserId", + table: "BlockedProfiles", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "BlockedProfiles"); + } + } +} diff --git a/ProjectLighthouse/PlayerData/Profiles/BlockedProfile.cs b/ProjectLighthouse/PlayerData/Profiles/BlockedProfile.cs new file mode 100644 index 00000000..53137c25 --- /dev/null +++ b/ProjectLighthouse/PlayerData/Profiles/BlockedProfile.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles; + +public class BlockedProfile +{ + [Key] + public int BlockedProfileId { get; set; } + + public int UserId { get; set; } + + [ForeignKey(nameof(UserId))] + public User User { get; set; } + + public int BlockedUserId { get; set; } + + [ForeignKey(nameof(BlockedUserId))] + public User BlockedUser { get; set; } +} \ No newline at end of file diff --git a/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs index 12f89fba..0353524e 100644 --- a/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs +++ b/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs @@ -609,6 +609,27 @@ namespace ProjectLighthouse.Migrations b.ToTable("PlatformLinkAttempts"); }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.BlockedProfile", b => + { + b.Property("BlockedProfileId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BlockedUserId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("BlockedProfileId"); + + b.HasIndex("BlockedUserId"); + + b.HasIndex("UserId"); + + b.ToTable("BlockedProfiles"); + }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Comment", b => { b.Property("CommentId") @@ -1225,6 +1246,25 @@ namespace ProjectLighthouse.Migrations b.Navigation("User"); }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.BlockedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "BlockedUser") + .WithMany() + .HasForeignKey("BlockedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BlockedUser"); + + b.Navigation("User"); + }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.Comment", b => { b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "Poster")