From 8f11ff86de18dffcd1b920f53db4950445a6a35e Mon Sep 17 00:00:00 2001 From: Slendy Date: Fri, 7 Jul 2023 13:10:21 -0500 Subject: [PATCH 01/16] Initial work for iFrame pagination --- .../Controllers/CommentController.cs | 82 ++++++++++++ .../Controllers/SlotPageController.cs | 43 ------- .../Controllers/UserPageController.cs | 47 ------- .../Pages/Frames/CommentFrame.cshtml | 26 ++++ .../Pages/Frames/CommentFrame.cshtml.cs | 89 +++++++++++++ .../Pages/Frames/PhotoFrame.cshtml | 37 ++++++ .../Pages/Frames/PhotoFrame.cshtml.cs | 40 ++++++ .../Pages/Frames/SlotsFrame.cshtml | 27 ++++ .../Pages/Frames/SlotsFrame.cshtml.cs | 32 +++++ .../Pages/Layouts/BaseFrame.cshtml | 21 +++ .../Pages/Layouts/BaseFrame.cshtml.cs | 59 +++++++++ .../Pages/Layouts/BaseLayout.cshtml | 8 ++ .../Pages/Partials/CommentsPartial.cshtml | 11 +- .../Pages/UserPage.cshtml | 121 +++++++----------- .../Startup/WebsiteStartup.cs | 11 ++ 15 files changed, 485 insertions(+), 169 deletions(-) create mode 100644 ProjectLighthouse.Servers.Website/Controllers/CommentController.cs create mode 100644 ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml create mode 100644 ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs create mode 100644 ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml create mode 100644 ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml.cs create mode 100644 ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml create mode 100644 ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml.cs create mode 100644 ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml create mode 100644 ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml.cs diff --git a/ProjectLighthouse.Servers.Website/Controllers/CommentController.cs b/ProjectLighthouse.Servers.Website/Controllers/CommentController.cs new file mode 100644 index 000000000..d7c40b193 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Controllers/CommentController.cs @@ -0,0 +1,82 @@ +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Database; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.Types.Entities.Profile; +using LBPUnion.ProjectLighthouse.Types.Entities.Token; +using LBPUnion.ProjectLighthouse.Types.Logging; +using Microsoft.AspNetCore.Mvc; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers; + +[Route("{type}/{id:int}")] +public class CommentController : ControllerBase +{ + private readonly DatabaseContext database; + + public CommentController(DatabaseContext database) + { + this.database = database; + } + + private static CommentType? ParseType(string type) => + type switch + { + "slot" => CommentType.Level, + "user" => CommentType.Profile, + _ => null, + }; + + [HttpGet("rateComment")] + public async Task RateComment(string type, int id, [FromQuery] int? commentId, [FromQuery] int? rating, [FromQuery] string? redirect) + { + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); + + CommentType? commentType = ParseType(type); + if (commentType == null) return this.BadRequest(); + + await this.database.RateComment(token.UserId, commentId.GetValueOrDefault(), rating.GetValueOrDefault()); + + return this.Redirect(redirect ?? $"~/user/{id}#{commentId}"); + } + + [HttpPost("postComment")] + public async Task PostComment(string type, int id, [FromForm] string? msg, [FromQuery] string? redirect) + { + WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); + if (token == null) return this.Redirect("~/login"); + + CommentType? commentType = ParseType(type); + if (commentType == null) return this.BadRequest(); + + if (msg == null) + { + Logger.Error($"Refusing to post comment from {token.UserId} on {commentType} {id}, {nameof(msg)} is null", + LogArea.Comments); + return this.Redirect("~/user/" + id); + } + + string username = await this.database.UsernameFromWebToken(token); + string filteredText = CensorHelper.FilterMessage(msg); + + if (ServerConfiguration.Instance.LogChatFiltering && filteredText != msg) + Logger.Info( + $"Censored profane word(s) from {commentType} comment sent by {username}: \"{msg}\" => \"{filteredText}\"", + LogArea.Filter); + + bool success = await this.database.PostComment(token.UserId, id, (CommentType)commentType, filteredText); + if (success) + { + Logger.Success($"Posted comment from {username}: \"{filteredText}\" on {commentType} {id}", + LogArea.Comments); + } + else + { + Logger.Error($"Failed to post comment from {username}: \"{filteredText}\" on {commentType} {id}", + LogArea.Comments); + } + + return this.Redirect(redirect ?? $"~/{type}/" + id); + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs b/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs index 66eafbc08..2db16becb 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/SlotPageController.cs @@ -46,49 +46,6 @@ public async Task UnpublishSlot([FromRoute] int id) return this.Redirect("~/slots/0"); } - [HttpGet("rateComment")] - public async Task RateComment([FromRoute] int id, [FromQuery] int commentId, [FromQuery] int rating) - { - WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); - if (token == null) return this.Redirect("~/login"); - - await this.database.RateComment(token.UserId, commentId, rating); - - return this.Redirect($"~/slot/{id}#{commentId}"); - } - - [HttpPost("postComment")] - public async Task PostComment([FromRoute] int id, [FromForm] string? msg) - { - WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); - if (token == null) return this.Redirect("~/login"); - - if (msg == null) - { - Logger.Error($"Refusing to post comment from {token.UserId} on level {id}, {nameof(msg)} is null", LogArea.Comments); - return this.Redirect("~/slot/" + id); - } - - string username = await this.database.UsernameFromWebToken(token); - string filteredText = CensorHelper.FilterMessage(msg); - - if (ServerConfiguration.Instance.LogChatFiltering && filteredText != msg) - Logger.Info($"Censored profane word(s) from slot comment sent by {username}: \"{msg}\" => \"{filteredText}\"", - LogArea.Filter); - - bool success = await this.database.PostComment(token.UserId, id, CommentType.Level, filteredText); - if (success) - { - Logger.Success($"Posted comment from {username}: \"{filteredText}\" on level {id}", LogArea.Comments); - } - else - { - Logger.Error($"Failed to post comment from {username}: \"{filteredText}\" on level {id}", LogArea.Comments); - } - - return this.Redirect("~/slot/" + id); - } - [HttpGet("heart")] public async Task HeartLevel([FromRoute] int id, [FromQuery] string? callbackUrl) { diff --git a/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs index 161bb1a87..a124d3397 100644 --- a/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs +++ b/ProjectLighthouse.Servers.Website/Controllers/UserPageController.cs @@ -1,11 +1,7 @@ #nullable enable -using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Database; -using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Token; -using LBPUnion.ProjectLighthouse.Types.Logging; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -22,49 +18,6 @@ public UserPageController(DatabaseContext database) this.database = database; } - [HttpGet("rateComment")] - public async Task RateComment([FromRoute] int id, [FromQuery] int? commentId, [FromQuery] int? rating) - { - WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); - if (token == null) return this.Redirect("~/login"); - - await this.database.RateComment(token.UserId, commentId.GetValueOrDefault(), rating.GetValueOrDefault()); - - return this.Redirect($"~/user/{id}#{commentId}"); - } - - [HttpPost("postComment")] - public async Task PostComment([FromRoute] int id, [FromForm] string? msg) - { - WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request); - if (token == null) return this.Redirect("~/login"); - - if (msg == null) - { - Logger.Error($"Refusing to post comment from {token.UserId} on user {id}, {nameof(msg)} is null", LogArea.Comments); - return this.Redirect("~/user/" + id); - } - - string username = await this.database.UsernameFromWebToken(token); - string filteredText = CensorHelper.FilterMessage(msg); - - if (ServerConfiguration.Instance.LogChatFiltering && filteredText != msg) - Logger.Info($"Censored profane word(s) from user comment sent by {username}: \"{msg}\" => \"{filteredText}\"", - LogArea.Filter); - - bool success = await this.database.PostComment(token.UserId, id, CommentType.Profile, filteredText); - if (success) - { - Logger.Success($"Posted comment from {username}: \"{filteredText}\" on user {id}", LogArea.Comments); - } - else - { - Logger.Error($"Failed to post comment from {username}: \"{filteredText}\" on user {id}", LogArea.Comments); - } - - return this.Redirect("~/user/" + id); - } - [HttpGet("heart")] public async Task HeartUser([FromRoute] int id) { diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml new file mode 100644 index 000000000..26ea5954c --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml @@ -0,0 +1,26 @@ +@page "/comments/{type}/{id:int}" +@using LBPUnion.ProjectLighthouse.Servers.Website.Extensions +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames.CommentFrame + +@{ + Model.Title = "Comments"; + Layout = "Layouts/BaseFrame"; + + string language = Model.GetLanguage(); + string timeZone = Model.GetTimeZone(); + string type = (string?)RouteData.Values["type"] ?? ""; + _ = int.TryParse((string?)RouteData.Values["id"], out int id); +} + +@await Html.PartialAsync("Partials/CommentsPartial", new ViewDataDictionary(ViewData.WithLang(language).WithTime(timeZone)) + { + { + "PageOwner", Model.PageOwner + }, + { + "BaseUrl", $"/{type}/{id}" + }, + { + "RedirectUrl", $"/comments/{type}/{id}" + }, + }) \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs new file mode 100644 index 000000000..73177d8a3 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs @@ -0,0 +1,89 @@ +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Database; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types.Entities.Interaction; +using LBPUnion.ProjectLighthouse.Types.Entities.Level; +using LBPUnion.ProjectLighthouse.Types.Entities.Profile; +using LBPUnion.ProjectLighthouse.Types.Users; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames; + +public class CommentFrame : BaseFrame +{ + public CommentFrame(DatabaseContext database) : base(database) + { } + + public Dictionary Comments = new(); + + public bool CommentsEnabled { get; set; } + + public int PageOwner { get; set; } + + public async Task OnGet(string type, int id) + { + CommentType? commentType = type switch + { + "slot" => CommentType.Level, + "user" => CommentType.Profile, + _ => null, + }; + switch (commentType) + { + case CommentType.Level: + { + this.CommentsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled; + SlotEntity? slot = await this.Database.Slots.FindAsync(id); + if (slot == null) return this.BadRequest(); + this.PageOwner = slot.CreatorId; + this.CommentsEnabled &= slot.CommentsEnabled; + break; + } + case CommentType.Profile: + { + this.CommentsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled; + UserEntity? user = await this.Database.Users.FindAsync(id); + if (user == null) return this.BadRequest(); + this.PageOwner = user.UserId; + this.CommentsEnabled &= user.CommentsEnabled; + break; + } + default: return this.BadRequest(); + } + + 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(c => c.TargetId == id && c.Type == commentType) + .Where(c => !blockedUsers.Contains(c.PosterUserId)) + .Include(c => c.Poster) + .Where(c => c.Poster.PermissionLevel != PermissionLevel.Banned) + .Take(50) + .ToDictionaryAsync(c => c, _ => (RatedCommentEntity?)null); + + if (this.User == null) return this.Page(); + + foreach (KeyValuePair kvp in this.Comments) + { + RatedCommentEntity? reaction = await this.Database.RatedComments.FirstOrDefaultAsync(r => + r.UserId == this.User.UserId && r.CommentId == kvp.Key.CommentId); + this.Comments[kvp.Key] = reaction; + } + } + else + { + this.Comments = new Dictionary(); + } + + return this.Page(); + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml new file mode 100644 index 000000000..462a0c12b --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml @@ -0,0 +1,37 @@ +@page "/photos/{type}/{id:int}" +@using LBPUnion.ProjectLighthouse.Servers.Website.Extensions +@using LBPUnion.ProjectLighthouse.Types.Entities.Profile +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames.PhotoFrame + +@{ + Layout = "Layouts/BaseFrame"; + Model.Title = "Photos"; + string language = Model.GetLanguage(); + string timeZone = Model.GetTimeZone(); + bool isMobile = Model.IsMobile; +} + +
+ + @if (Model.Photos.Count > 0) + { +
+ @foreach (PhotoEntity photo in Model.Photos) + { + string width = isMobile ? "sixteen" : "eight"; + bool canDelete = Model.User != null && (Model.User.IsModerator || Model.User.UserId == photo.CreatorId); +
+ @await photo.ToHtml(Html, ViewData, language, timeZone, canDelete) +
+ } +
+ @if (isMobile) + { +
+ } + } + else + { +

This user hasn't uploaded any photos

+ } +
\ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml.cs new file mode 100644 index 000000000..1640103df --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml.cs @@ -0,0 +1,40 @@ +using LBPUnion.ProjectLighthouse.Database; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types.Entities.Profile; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames; + +public class PhotoFrame : BaseFrame +{ + public List Photos { get; set; } = new(); + + public PhotoFrame(DatabaseContext database) : base(database) + { } + + public async Task OnGet(string type, int id) + { + if (type != "user" && type != "slot") return this.BadRequest(); + + IQueryable photoQuery = this.Database.Photos.Include(p => p.Slot) + .Include(p => p.PhotoSubjects) + .ThenInclude(ps => ps.User) + .OrderByDescending(p => p.Timestamp); + + switch (type) + { + case "user": + photoQuery = photoQuery.Where(p => p.CreatorId == id); + break; + + case "slot": + photoQuery = photoQuery.Where(p => p.SlotId == id); + break; + } + + this.Photos = await photoQuery.Take(6).ToListAsync(); + + return this.Page(); + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml new file mode 100644 index 000000000..6eb192b50 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml @@ -0,0 +1,27 @@ +@page "/slots/{route}/{id:int}" +@using LBPUnion.ProjectLighthouse.Servers.Website.Extensions +@using LBPUnion.ProjectLighthouse.Types.Entities.Level +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames.SlotsFrame + +@{ + Layout = "Layouts/BaseFrame"; + Model.Title = "Photos"; + string language = Model.GetLanguage(); + string timeZone = Model.GetTimeZone(); + bool isMobile = Model.IsMobile; + string route = (string?)RouteData.Values["route"] ?? ""; + _ = int.TryParse((string?)RouteData.Values["id"], out int id); +} + +
+ @if (Model.Slots.Count == 0) + { +

This user hasn't published any levels

+ } + @foreach (SlotEntity slot in Model.Slots) + { +
+ @await slot.ToHtml(Html, ViewData, Model.User, $"~/slots/{route}/{id}", language, timeZone, isMobile, true) +
+ } +
\ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml.cs new file mode 100644 index 000000000..5f316af61 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml.cs @@ -0,0 +1,32 @@ +using LBPUnion.ProjectLighthouse.Database; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types.Entities.Level; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames; + +public class SlotsFrame : BaseFrame +{ + public List Slots = new(); + + public SlotsFrame(DatabaseContext database) : base(database) + { } + + public async Task OnGet(string route, int id) + { + IQueryable slotsQuery = this.Database.Slots.AsQueryable(); + + switch (route) + { + case "by": + slotsQuery = this.Database.Slots.Include(p => p.Creator) + .OrderByDescending(s => s.LastUpdated) + .Where(p => p.CreatorId == id); + break; + } + + this.Slots = await slotsQuery.Take(10).ToListAsync(); + return this.Page(); + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml new file mode 100644 index 000000000..d22e4b0bc --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml @@ -0,0 +1,21 @@ +@using LBPUnion.ProjectLighthouse.Extensions +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts.BaseFrame + + + +@{ + Model.IsMobile = Model.Request.IsMobile(); +} + + + + @Model.Title + + + + +
+ @RenderBody() +
+ + \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml.cs new file mode 100644 index 000000000..3cc953ca3 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml.cs @@ -0,0 +1,59 @@ +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Database; +using LBPUnion.ProjectLighthouse.Localization; +using LBPUnion.ProjectLighthouse.Types.Entities.Profile; +using Microsoft.AspNetCore.Localization; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; + +public class BaseFrame : PageModel +{ + public readonly DatabaseContext Database; + + public BaseFrame(DatabaseContext database) + { + this.Database = database; + } + + public string Title = string.Empty; + public bool IsMobile; + + private UserEntity? user; + public new UserEntity? User + { + get + { + if (this.user != null) return this.user; + + return this.user = this.Database.UserFromWebRequest(this.Request); + } + set => this.user = value; + } + + private string? language; + private string? timeZone; + + public string GetLanguage() + { + if (ServerStatics.IsUnitTesting) return LocalizationManager.DefaultLang; + if (this.language != null) return this.language; + + if (this.User != null) return this.language = this.User.Language; + + IRequestCultureFeature? requestCulture = this.Request.HttpContext.Features.Get(); + if (requestCulture == null) return this.language = LocalizationManager.DefaultLang; + + return this.language = requestCulture.RequestCulture.UICulture.Name; + } + + public string GetTimeZone() + { + if (ServerStatics.IsUnitTesting) return TimeZoneInfo.Local.Id; + if (this.timeZone != null) return this.timeZone; + + string userTimeZone = this.User?.TimeZone ?? TimeZoneInfo.Local.Id; + + return this.timeZone = userTimeZone; + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml index 89ff3a8bd..f6257a87a 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml @@ -63,6 +63,14 @@ } + + @* If non-frame page is loaded in iFrame, redirect top level window *@ + @* Google Analytics *@ @if (ServerConfiguration.Instance.GoogleAnalytics.AnalyticsEnabled) diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml index 18d66580c..3d81839fe 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/CommentsPartial.cshtml @@ -12,6 +12,8 @@ string timeZone = (string?)ViewData["TimeZone"] ?? TimeZoneInfo.Local.Id; TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone); int pageOwnerId = (int?)ViewData["PageOwner"] ?? 0; + string baseUrl = (string?)ViewData["BaseUrl"] ?? Url.RouteUrl(ViewContext.RouteData.Values) ?? "~/"; + string? redirectUrl = (string?)ViewData["RedirectUrl"]; }
@@ -32,7 +34,8 @@ @if (Model.CommentsEnabled && Model.User != null) {
-
+ +
@@ -55,8 +58,6 @@ HttpUtility.HtmlDecode(comment.GetCommentMessage(Database), messageWriter); string decodedMessage = messageWriter.ToString(); - string? url = Url.RouteUrl(ViewContext.RouteData.Values); - if (url == null) continue; int rating = comment.ThumbsUp - comment.ThumbsDown; @@ -69,11 +70,11 @@ } }
- + @(rating) - +
diff --git a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml index 99d5588d1..69921c28f 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml @@ -4,7 +4,6 @@ @using LBPUnion.ProjectLighthouse.Localization.StringLists @using LBPUnion.ProjectLighthouse.Servers.Website.Extensions @using LBPUnion.ProjectLighthouse.Types.Entities.Level -@using LBPUnion.ProjectLighthouse.Types.Entities.Profile @using LBPUnion.ProjectLighthouse.Types.Moderation.Cases @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.UserPage @@ -27,12 +26,13 @@ @if (Model.User != null && Model.User.IsModerator) { Reason: - "@Model.ProfileUser.BannedReason"
+ "@Model.ProfileUser.BannedReason" +

Only you and other moderators may view the ban reason.

} - else + else {

This user has been banned for violating the Terms of Service. Remember to follow the rules!

} @@ -42,20 +42,20 @@
@await Html.PartialAsync("Partials/UserCardPartial", Model.ProfileUser, new ViewDataDictionary(ViewData) - { - { - "ShowLink", false - }, - { - "IsMobile", Model.Request.IsMobile() - }, - { - "Language", Model.GetLanguage() - }, - { - "TimeZone", Model.GetTimeZone() - }, - }) + { + { + "ShowLink", false + }, + { + "IsMobile", Model.Request.IsMobile() + }, + { + "Language", Model.GetLanguage() + }, + { + "TimeZone", Model.GetTimeZone() + }, + })

@@ -140,12 +140,8 @@
@{ - string outerDiv = isMobile ? - "horizontal-scroll" : - "three wide column"; - string innerDiv = isMobile ? - "ui top attached tabular menu horizontal-scroll" : - "ui vertical fluid tabular menu"; + string outerDiv = isMobile ? "horizontal-scroll" : "three wide column"; + string innerDiv = isMobile ? "ui top attached tabular menu horizontal-scroll" : "ui vertical fluid tabular menu"; }
@@ -155,7 +151,6 @@ @Model.Translate(BaseLayoutStrings.HeaderPhotos) - @Model.Translate(BaseLayoutStrings.HeaderSlots) @@ -178,56 +173,28 @@ }
- @if (Model.ProfileUser.IsBanned) - { -
-

Comments are disabled because the user is banned.

-
- } - else - { - @await Html.PartialAsync("Partials/CommentsPartial", new ViewDataDictionary(ViewData.WithLang(language).WithTime(timeZone)) - { {"PageOwner", Model.ProfileUser.UserId}, }) - } +
-
- @if (Model.Photos != null && Model.Photos.Count != 0) - { -
- @foreach (PhotoEntity photo in Model.Photos) - { - string width = isMobile ? "sixteen" : "eight"; - bool canDelete = Model.User != null && (Model.User.IsModerator || Model.User.UserId == photo.CreatorId); -
- @await photo.ToHtml(Html, ViewData, language, timeZone, canDelete) -
- } -
- @if (isMobile) - { -
- } - } - else - { -

This user hasn't uploaded any photos

- } -
+
-
- @if (Model.Slots?.Count == 0) - { -

This user hasn't published any levels

- } - @foreach (SlotEntity slot in Model.Slots ?? new List()) - { -
- @await slot.ToHtml(Html, ViewData, Model.User, $"~/user/{Model.ProfileUser.UserId}#levels", language, timeZone, isMobile, true) -
- } -
+
@@ -291,7 +258,7 @@
} - + @if (Model.ProfileUser.CommentsEnabled) {
@@ -302,7 +269,7 @@
} - + - + @if (Model.User.IsAdmin) { @await Html.PartialAsync("Partials/AdminSetGrantedSlotsFormPartial", Model.ProfileUser) @@ -344,6 +311,12 @@ function setVisible(e){ } // unhide content eTarget.style.display = ""; + let frame = eTarget.children[0] || undefined; + if (frame !== undefined && frame.nodeName === "IFRAME"){ + console.log(eTarget.id + " - frame not null - " + frame.contentWindow.document.body.scrollHeight); + frame.style.height = frame.contentWindow.document.body.scrollHeight + "px"; + } + console.dir(eTarget); e.classList.add("active"); } @@ -367,4 +340,4 @@ if (selectedElement != null) { let sidebarEle = document.querySelector("[target=" + selectedElement.id + "]") setVisible(sidebarEle); } - + \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Startup/WebsiteStartup.cs b/ProjectLighthouse.Servers.Website/Startup/WebsiteStartup.cs index c391fc5e3..12c463696 100644 --- a/ProjectLighthouse.Servers.Website/Startup/WebsiteStartup.cs +++ b/ProjectLighthouse.Servers.Website/Startup/WebsiteStartup.cs @@ -38,6 +38,16 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllers(); #if DEBUG + // Add CORS for debugging + services.AddCors(options => + { + options.AddDefaultPolicy(policy => + { + policy.AllowAnyHeader(); + policy.AllowAnyMethod(); + policy.AllowAnyOrigin(); + }); + }); services.AddRazorPages().WithRazorPagesAtContentRoot().AddRazorRuntimeCompilation((options) => { // jank but works @@ -112,6 +122,7 @@ public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env) { #if DEBUG app.UseDeveloperExceptionPage(); + app.UseCors(); #endif app.UseStatusCodePagesWithReExecute("/404"); From 2a74aaadf8c5d8134d6f418744f514fbb08bcf13 Mon Sep 17 00:00:00 2001 From: Slendy Date: Fri, 7 Jul 2023 13:22:24 -0500 Subject: [PATCH 02/16] Add allow-same-origin because we are using relative paths it should be safe --- ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml index 69921c28f..b1d90964c 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml @@ -176,7 +176,7 @@
@@ -184,7 +184,7 @@
@@ -192,7 +192,7 @@
From 4679777759c3103a5d28202e5af94d2db4c6e198 Mon Sep 17 00:00:00 2001 From: Slendy Date: Fri, 7 Jul 2023 13:45:14 -0500 Subject: [PATCH 03/16] There's no point in sandboxing with allow-scripts and allow-same-origin --- .../Pages/Layouts/BaseLayout.cshtml | 6 +++++- ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml | 5 +---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml index f6257a87a..251cd7d14 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml @@ -67,8 +67,12 @@ @* If non-frame page is loaded in iFrame, redirect top level window *@ diff --git a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml index b1d90964c..beb59c81f 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml @@ -172,11 +172,10 @@ string divLength = isMobile ? "sixteen" : "thirteen"; }
-
+
@@ -184,7 +183,6 @@
@@ -192,7 +190,6 @@
From 83196b8f26cab63c7551a4e8ce3fa61a19be6ab9 Mon Sep 17 00:00:00 2001 From: Slendy Date: Fri, 7 Jul 2023 20:56:20 -0500 Subject: [PATCH 04/16] Completely refactor slots and users to use iFrame for pagination --- .../Controllers/CommentController.cs | 5 +- .../Pages/Frames/CommentFrame.cshtml | 43 ++-- .../Pages/Frames/CommentFrame.cshtml.cs | 39 ++-- .../Pages/Frames/PaginatedFrame.cshtml | 33 ++++ .../Pages/Frames/PaginatedFrame.cshtml.cs | 30 +++ .../Pages/Frames/PhotoFrame.cshtml | 5 +- .../Pages/Frames/PhotoFrame.cshtml.cs | 20 +- .../Pages/Frames/ReviewFrame.cshtml | 35 ++++ .../Pages/Frames/ReviewFrame.cshtml.cs | 50 +++++ .../Pages/Frames/ScoreFrame.cshtml | 23 +++ .../Pages/Frames/ScoreFrame.cshtml.cs | 60 ++++++ .../Pages/Frames/SlotsFrame.cshtml | 20 +- .../Pages/Frames/SlotsFrame.cshtml.cs | 58 +++++- .../Pages/Layouts/BaseFrame.cshtml | 2 +- .../Pages/Layouts/BaseLayout.cshtml | 2 - .../Pages/Partials/CommentsPartial.cshtml | 155 +++++++-------- .../Pages/Partials/FramePartial.cshtml | 9 + .../Pages/Partials/LeaderboardPartial.cshtml | 46 ++--- .../Partials/Links/UserLinkPartial.cshtml | 1 - .../Pages/Partials/ReviewPartial.cshtml | 186 ++++++++---------- .../Partials/SectionScriptPartial.cshtml | 51 +++++ .../Pages/SlotPage.cshtml | 91 +-------- .../Pages/SlotPage.cshtml.cs | 73 +------ .../Pages/UserPage.cshtml | 115 +---------- .../Pages/UserPage.cshtml.cs | 73 ------- .../Database/DatabaseContext.GameTokens.cs | 24 +++ ...ntext.Utils.cs => DatabaseContext.User.cs} | 60 +++--- .../Types/Entities/Level/ReviewEntity.cs | 5 +- .../Types/Entities/Profile/CommentEntity.cs | 6 +- 29 files changed, 661 insertions(+), 659 deletions(-) create mode 100644 ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml create mode 100644 ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs create mode 100644 ProjectLighthouse.Servers.Website/Pages/Frames/ReviewFrame.cshtml create mode 100644 ProjectLighthouse.Servers.Website/Pages/Frames/ReviewFrame.cshtml.cs create mode 100644 ProjectLighthouse.Servers.Website/Pages/Frames/ScoreFrame.cshtml create mode 100644 ProjectLighthouse.Servers.Website/Pages/Frames/ScoreFrame.cshtml.cs create mode 100644 ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml create mode 100644 ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml rename ProjectLighthouse/Database/{DatabaseContext.Utils.cs => DatabaseContext.User.cs} (74%) diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs index 15d065440..c85247273 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/CommentController.cs @@ -81,10 +81,7 @@ public async Task GetComments(string? username, string? slotType, if (targetId == 0) return this.NotFound(); - List blockedUsers = await ( - from blockedProfile in this.database.BlockedProfiles - where blockedProfile.UserId == token.UserId - select blockedProfile.BlockedUserId).ToListAsync(); + List blockedUsers = await this.database.GetBlockedUsers(token.UserId); List comments = (await this.database.Comments.Where(p => p.TargetId == targetId && p.Type == type) .OrderByDescending(p => p.Timestamp) diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml index 26ea5954c..9e6cc6092 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml @@ -4,23 +4,36 @@ @{ Model.Title = "Comments"; - Layout = "Layouts/BaseFrame"; + Layout = "PaginatedFrame"; string language = Model.GetLanguage(); string timeZone = Model.GetTimeZone(); - string type = (string?)RouteData.Values["type"] ?? ""; - _ = int.TryParse((string?)RouteData.Values["id"], out int id); } -@await Html.PartialAsync("Partials/CommentsPartial", new ViewDataDictionary(ViewData.WithLang(language).WithTime(timeZone)) - { - { - "PageOwner", Model.PageOwner - }, - { - "BaseUrl", $"/{type}/{id}" - }, - { - "RedirectUrl", $"/comments/{type}/{id}" - }, - }) \ No newline at end of file +
+ @if (Model.Comments.Count == 0 && Model.CommentsEnabled) + { +

There are no comments.

+ } + else if (!Model.CommentsEnabled) + { + Comments are disabled. + } + else + { + int count = Model.TotalItems; +

There @(count == 1 ? "is" : "are") @count comment@(count == 1 ? "" : "s").

+ @await Html.PartialAsync("Partials/CommentsPartial", (Model.User, Model.Comments, Model.CommentsEnabled), new ViewDataDictionary(ViewData.WithLang(language).WithTime(timeZone)) + { + { + "PageOwner", Model.PageOwner + }, + { + "BaseUrl", $"/{Model.Type}/{Model.Id}" + }, + { + "RedirectUrl", $"/comments/{Model.Type}/{Model.Id}" + }, + }) + } +
\ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs index 73177d8a3..a6faff947 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs @@ -1,6 +1,6 @@ using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Database; -using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Types.Entities.Interaction; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; @@ -10,19 +10,27 @@ namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames; -public class CommentFrame : BaseFrame +public class CommentFrame : PaginatedFrame { public CommentFrame(DatabaseContext database) : base(database) - { } + { + this.ItemsPerPage = 10; + } public Dictionary Comments = new(); + public int Id { get; set; } + public string Type { get; set; } = ""; + public bool CommentsEnabled { get; set; } public int PageOwner { get; set; } - public async Task OnGet(string type, int id) + public async Task OnGet([FromQuery] int page, string type, int id) { + this.Type = type; + this.Id = id; + this.CurrentPage = page; CommentType? commentType = type switch { "slot" => CommentType.Level, @@ -54,20 +62,19 @@ public async Task OnGet(string type, int id) 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(); + List blockedUsers = await this.Database.GetBlockedUsers(this.User?.UserId); - this.Comments = await this.Database.Comments.Include(p => p.Poster) - .OrderByDescending(p => p.Timestamp) - .Where(c => c.TargetId == id && c.Type == commentType) - .Where(c => !blockedUsers.Contains(c.PosterUserId)) - .Include(c => c.Poster) + IQueryable commentQuery = this.Database.Comments.Include(p => p.Poster) .Where(c => c.Poster.PermissionLevel != PermissionLevel.Banned) - .Take(50) + .Where(c => c.TargetId == id && c.Type == commentType) + .Where(c => !blockedUsers.Contains(c.PosterUserId)); + + this.TotalItems = await commentQuery.CountAsync(); + + this.ClampPage(); + + this.Comments = await commentQuery.OrderByDescending(c => c.Timestamp) + .ApplyPagination(this.PageData) .ToDictionaryAsync(c => c, _ => (RatedCommentEntity?)null); if (this.User == null) return this.Page(); diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml new file mode 100644 index 000000000..40dbc51be --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml @@ -0,0 +1,33 @@ +@using LBPUnion.ProjectLighthouse.Extensions +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames.PaginatedFrame + + + +@{ + Model.IsMobile = Model.Request.IsMobile(); +} + + + + @Model.Title + + + + +
+ @RenderBody() + +
+ @if (Model.CurrentPage > 1) + { + Previous Page + } + Page @Model.CurrentPage / @Model.TotalPages + @if (Model.CurrentPage < Model.TotalPages) + { + Next Page + } +
+
+ + \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs new file mode 100644 index 000000000..9435e69ba --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs @@ -0,0 +1,30 @@ +using LBPUnion.ProjectLighthouse.Database; +using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types.Filter; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames; + +public abstract class PaginatedFrame : BaseFrame +{ + protected PaginatedFrame(DatabaseContext database) : base(database) + { } + + public int CurrentPage { get; set; } + public int TotalItems { get; set; } + public int ItemsPerPage { get; set; } + public int TotalPages => Math.Max(1, this.TotalItems / this.ItemsPerPage); + + public PaginationData PageData => + new() + { + MaxElements = this.ItemsPerPage, + PageSize = this.ItemsPerPage, + PageStart = this.CurrentPage, + TotalElements = this.TotalItems, + }; + + /// + /// Only call after setting CurrentPage and TotalItems + /// + public void ClampPage() => this.CurrentPage = Math.Clamp(this.CurrentPage, 1, this.TotalPages); +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml index 462a0c12b..3ae6c01ab 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml @@ -4,7 +4,7 @@ @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames.PhotoFrame @{ - Layout = "Layouts/BaseFrame"; + Layout = "PaginatedFrame"; Model.Title = "Photos"; string language = Model.GetLanguage(); string timeZone = Model.GetTimeZone(); @@ -12,7 +12,6 @@ }
- @if (Model.Photos.Count > 0) {
@@ -32,6 +31,6 @@ } else { -

This user hasn't uploaded any photos

+

There are no photos

}
\ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml.cs index 1640103df..eeee53d67 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/PhotoFrame.cshtml.cs @@ -1,26 +1,28 @@ using LBPUnion.ProjectLighthouse.Database; -using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames; -public class PhotoFrame : BaseFrame +public class PhotoFrame : PaginatedFrame { public List Photos { get; set; } = new(); public PhotoFrame(DatabaseContext database) : base(database) - { } + { + this.ItemsPerPage = 10; + } - public async Task OnGet(string type, int id) + public async Task OnGet([FromQuery] int page, string type, int id) { + this.CurrentPage = page; if (type != "user" && type != "slot") return this.BadRequest(); IQueryable photoQuery = this.Database.Photos.Include(p => p.Slot) .Include(p => p.PhotoSubjects) - .ThenInclude(ps => ps.User) - .OrderByDescending(p => p.Timestamp); + .ThenInclude(ps => ps.User); switch (type) { @@ -33,7 +35,11 @@ public async Task OnGet(string type, int id) break; } - this.Photos = await photoQuery.Take(6).ToListAsync(); + this.TotalItems = await photoQuery.CountAsync(); + + this.ClampPage(); + + this.Photos = await photoQuery.OrderByDescending(p => p.Timestamp).ApplyPagination(this.PageData).ToListAsync(); return this.Page(); } diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/ReviewFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Frames/ReviewFrame.cshtml new file mode 100644 index 000000000..e894b929f --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/ReviewFrame.cshtml @@ -0,0 +1,35 @@ +@page "/reviews/{slotId:int}" +@using LBPUnion.ProjectLighthouse.Extensions +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames.ReviewFrame + +@{ + Layout = "PaginatedFrame"; +} + +
+ @if (Model.Reviews.Count == 0 && Model.ReviewsEnabled) + { +

There are no reviews.

+ } + else if (!Model.ReviewsEnabled) + { + + Reviews are disabled on this level. + + } + else + { + int count = Model.TotalItems; +

There @(count == 1 ? "is" : "are") @count review@(count == 1 ? "" : "s").

+
+ @await Html.PartialAsync("Partials/ReviewPartial", Model.Reviews, new ViewDataDictionary(ViewData) + { + { + "isMobile", Request.IsMobile() + }, + { + "CanDelete", Model.User?.IsModerator ?? false + }, + }) + } +
\ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/ReviewFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/ReviewFrame.cshtml.cs new file mode 100644 index 000000000..37f7c6f2d --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/ReviewFrame.cshtml.cs @@ -0,0 +1,50 @@ +using LBPUnion.ProjectLighthouse.Configuration; +using LBPUnion.ProjectLighthouse.Database; +using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.Types.Entities.Level; +using LBPUnion.ProjectLighthouse.Types.Users; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames; + +public class ReviewFrame : PaginatedFrame +{ + public List Reviews = new(); + + public bool ReviewsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelReviewsEnabled; + + public ReviewFrame(DatabaseContext database) : base(database) + { + this.ItemsPerPage = 10; + } + + public async Task OnGet([FromQuery] int page, int slotId) + { + this.CurrentPage = page; + + SlotEntity? slot = await this.Database.Slots.FindAsync(slotId); + if (slot == null) return this.BadRequest(); + + if (!this.ReviewsEnabled) return this.Page(); + + List blockedUsers = await this.Database.GetBlockedUsers(this.User?.UserId); + + IQueryable reviewQuery = this.Database.Reviews.Where(r => r.SlotId == slotId) + .Where(r => !blockedUsers.Contains(r.ReviewerId)) + .Include(r => r.Reviewer) + .Where(r => r.Reviewer.PermissionLevel != PermissionLevel.Banned); + + this.TotalItems = await reviewQuery.CountAsync(); + + this.ClampPage(); + + this.Reviews = await reviewQuery.OrderByDescending(r => r.ThumbsUp - r.ThumbsDown) + .ThenByDescending(r => r.Timestamp) + .ApplyPagination(this.PageData) + .ToListAsync(); + + return this.Page(); + } + +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/ScoreFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Frames/ScoreFrame.cshtml new file mode 100644 index 000000000..d2a4c00a1 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/ScoreFrame.cshtml @@ -0,0 +1,23 @@ +@page "/scores/{slotId:int}/{scoreType:int?}" +@using LBPUnion.ProjectLighthouse.Servers.Website.Extensions +@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames.ScoreFrame + +@{ + Layout = "PaginatedFrame"; + string language = Model.GetLanguage(); + string timeZone = Model.GetTimeZone(); +} + +
+ @if (Model.TotalItems == 0) + { +

There are no scores.

+ } + else + { + int count = Model.TotalItems; +

There @(count == 1 ? "is" : "are") @count score@(count == 1 ? "" : "s").

+
+ @await Html.PartialAsync("Partials/LeaderboardPartial", (Model.Scores, Model.ScoreType), ViewData.WithLang(language).WithTime(timeZone).CanDelete(Model.User?.IsModerator ?? false)) + } +
\ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/ScoreFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/ScoreFrame.cshtml.cs new file mode 100644 index 000000000..49499c1d6 --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/ScoreFrame.cshtml.cs @@ -0,0 +1,60 @@ +using LBPUnion.ProjectLighthouse.Database; +using LBPUnion.ProjectLighthouse.Extensions; +using LBPUnion.ProjectLighthouse.Types.Entities.Level; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames; + +public class ScoreFrame : PaginatedFrame +{ + public List<(int Rank, ScoreEntity Score)> Scores = new(); + + public int ScoreType { get; set; } + + public ScoreFrame(DatabaseContext database) : base(database) + { + this.ItemsPerPage = 10; + } + + public async Task OnGet([FromQuery] int page, int slotId, int? scoreType) + { + this.CurrentPage = page; + SlotEntity? slot = await this.Database.Slots.FindAsync(slotId); + if (slot == null) return this.BadRequest(); + + scoreType ??= slot.LevelType switch + { + "versus" => 7, + _ => 1, + }; + + Func isValidFunc = slot.LevelType switch + { + "versus" => type => type == 7, + _ => type => type is >= 1 and <= 4, + }; + + if (!isValidFunc(scoreType)) return this.BadRequest(); + + IQueryable scoreQuery = this.Database.Scores.Where(s => s.SlotId == slotId) + .Where(s => s.Type == scoreType); + + this.TotalItems = await scoreQuery.CountAsync(); + + this.ClampPage(); + + this.Scores = (await scoreQuery.OrderByDescending(s => s.Points) + .ThenBy(s => s.ScoreId) + .Select(s => new + { + Rank = scoreQuery.Count(s2 => s2.Points > s.Points) + 1, + Score = s, + }) + .ApplyPagination(this.PageData) + .ToListAsync()).Select(s => (s.Rank, s.Score)) + .ToList(); + + return this.Page(); + } +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml index 6eb192b50..27121e6d5 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml @@ -4,24 +4,26 @@ @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames.SlotsFrame @{ - Layout = "Layouts/BaseFrame"; + Layout = "PaginatedFrame"; Model.Title = "Photos"; string language = Model.GetLanguage(); string timeZone = Model.GetTimeZone(); bool isMobile = Model.IsMobile; - string route = (string?)RouteData.Values["route"] ?? ""; - _ = int.TryParse((string?)RouteData.Values["id"], out int id); } -
+
@if (Model.Slots.Count == 0) { -

This user hasn't published any levels

+

@Model.SlotsEmptyText

} - @foreach (SlotEntity slot in Model.Slots) + else { -
- @await slot.ToHtml(Html, ViewData, Model.User, $"~/slots/{route}/{id}", language, timeZone, isMobile, true) -
+

@Model.SlotsPresentText

+ foreach (SlotEntity slot in Model.Slots) + { +
+ @await slot.ToHtml(Html, ViewData, Model.User, $"~/slots/{Model.Type}/{Model.Id}", language, timeZone, isMobile, true) +
+ } }
\ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml.cs index 5f316af61..b6d07a04e 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/SlotsFrame.cshtml.cs @@ -1,32 +1,76 @@ using LBPUnion.ProjectLighthouse.Database; -using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames; -public class SlotsFrame : BaseFrame +public class SlotsFrame : PaginatedFrame { public List Slots = new(); + public string Type { get; set; } = ""; + public int Id { get; set; } + public string Color { get; set; } = ""; + public string SlotsPresentText { get; set; } = ""; + public string SlotsEmptyText { get; set; } = ""; + public SlotsFrame(DatabaseContext database) : base(database) - { } + { + this.ItemsPerPage = 10; + } - public async Task OnGet(string route, int id) + public async Task OnGet([FromQuery] int page, string route, int id) { + this.Type = route; + this.Id = id; + this.CurrentPage = page; IQueryable slotsQuery = this.Database.Slots.AsQueryable(); switch (route) { case "by": slotsQuery = this.Database.Slots.Include(p => p.Creator) - .OrderByDescending(s => s.LastUpdated) - .Where(p => p.CreatorId == id); + .Where(p => p.CreatorId == id) + .OrderByDescending(s => s.LastUpdated); + this.Color = "green"; + this.SlotsEmptyText = "This user hasn't published any levels"; + this.SlotsPresentText = "This user has published {0} level{1}"; + break; + case "hearted": + slotsQuery = this.Database.HeartedLevels.Where(h => h.UserId == id). + OrderByDescending(h => h.HeartedLevelId) + .Include(h => h.Slot) + .Select(h => h.Slot); + this.Color = "pink"; + this.SlotsEmptyText = "You haven't hearted any levels"; + this.SlotsPresentText = "You have hearted {0} level{1}"; + break; + case "queued": + slotsQuery = this.Database.QueuedLevels.Where(q => q.UserId == id) + .OrderByDescending(q => q.QueuedLevelId) + .Include(q => q.Slot) + .Select(q => q.Slot); + this.Color = "yellow"; + this.SlotsEmptyText = "You haven't queued any levels"; + this.SlotsPresentText = "There are {0} level{1} in your queue"; break; } - this.Slots = await slotsQuery.Take(10).ToListAsync(); + this.TotalItems = await slotsQuery.CountAsync(); + + this.ClampPage(); + + if (this.TotalItems != 0) + { + this.SlotsPresentText = string.Format(this.SlotsPresentText, this.TotalItems, this.TotalItems == 1 ? "" : "s"); + } + + this.Slots = await slotsQuery + .ApplyPagination(this.PageData) + .ToListAsync(); + return this.Page(); } } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml index d22e4b0bc..2ade2eb94 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml @@ -15,7 +15,7 @@
- @RenderBody() + @RenderBody()
\ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml index 251cd7d14..2f14b6a85 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml @@ -67,8 +67,6 @@ @* If non-frame page is loaded in iFrame, redirect top level window *@ -
\ No newline at end of file + \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml new file mode 100644 index 000000000..b3dc53cdb --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml @@ -0,0 +1,9 @@ +@model (string Url, string Title) + + \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/LeaderboardPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/LeaderboardPartial.cshtml index 797fac6d2..99df063c6 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/LeaderboardPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/LeaderboardPartial.cshtml @@ -4,44 +4,35 @@ @using LBPUnion.ProjectLighthouse.Types.Entities.Level @using LBPUnion.ProjectLighthouse.Types.Entities.Profile @using Microsoft.EntityFrameworkCore +@model (List<(int Rank, ScoreEntity Score)> Scores, int scoreType) +@inject DatabaseContext Database @{ string language = (string?)ViewData["Language"] ?? LocalizationManager.DefaultLang; string timeZone = (string?)ViewData["TimeZone"] ?? TimeZoneInfo.Local.Id; bool canDelete = (bool?)ViewData["CanDelete"] ?? false; -} -
- @if (Model.Scores.Count == 0) +} + +
+ @for (int i = 0; i < Model.Scores.Count; i++) { -

There are no scores.

- } - else - { - int count = Model.Scores.Count; -

There @(count == 1 ? "is" : "are") @count score@(count == 1 ? "" : "s").

-
- } -
- @for(int i = 0; i < Model.Scores.Count; i++) - { - ScoreEntity score = Model.Scores[i]; - string[] playerIds = score.PlayerIds; - DatabaseContext database = Model.Database; + (int Rank, ScoreEntity Score) scoreAndRank = Model.Scores[i]; + string[] playerIds = scoreAndRank.Score.PlayerIds;
- @if(canDelete) + @if (canDelete) { - - } - @(i+1): - @score.Points points + + } + @(scoreAndRank.Rank): + @scoreAndRank.Score.Points points
@for (int j = 0; j < playerIds.Length; j++) { - UserEntity? user = await database.Users.FirstOrDefaultAsync(u => u.Username == playerIds[j]); + UserEntity? user = await Database.Users.FirstOrDefaultAsync(u => u.Username == playerIds[j]);
@@ -64,13 +55,12 @@
} } -
- -
\ No newline at end of file + \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/Links/UserLinkPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/Links/UserLinkPartial.cshtml index 4d889b336..30a21be4f 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/Links/UserLinkPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/Links/UserLinkPartial.cshtml @@ -25,5 +25,4 @@ { @Model.Username } - \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/ReviewPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/ReviewPartial.cshtml index 610342658..ee13af03d 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/ReviewPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/ReviewPartial.cshtml @@ -4,112 +4,94 @@ @using LBPUnion.ProjectLighthouse.Helpers @using LBPUnion.ProjectLighthouse.Types.Entities.Level @using LBPUnion.ProjectLighthouse.Types.Serialization +@model List @{ bool isMobile = (bool?)ViewData["IsMobile"] ?? false; bool canDelete = (bool?)ViewData["CanDelete"] ?? false; } -
-
- @if (Model.Reviews.Count == 0 && Model.ReviewsEnabled) - { -

There are no reviews.

- } - else if (!Model.ReviewsEnabled) - { - - Reviews are disabled on this level. - - } - else - { - int count = Model.Reviews.Count; -

There @(count == 1 ? "is" : "are") @count review@(count == 1 ? "" : "s").

-
- } - - @for(int i = 0; i < Model.Reviews.Count; i++) - { - ReviewEntity review = Model.Reviews[i]; - string faceHash = (review.Thumb switch { - -1 => review.Reviewer?.BooHash, - 0 => review.Reviewer?.MehHash, - 1 => review.Reviewer?.YayHash, +@for (int i = 0; i < Model.Count; i++) +{ + ReviewEntity review = Model[i]; + string faceHash = review.Thumb switch { + -1 => review.Reviewer?.BooHash, + 0 => review.Reviewer?.MehHash, + 1 => review.Reviewer?.YayHash, - _ => throw new ArgumentOutOfRangeException(), - }) ?? ""; + _ => throw new ArgumentOutOfRangeException(), + } ?? ""; - if (string.IsNullOrWhiteSpace(faceHash) || !FileHelper.ResourceExists(faceHash)) - { - faceHash = ServerConfiguration.Instance.WebsiteConfiguration.MissingIconHash; - } + if (string.IsNullOrWhiteSpace(faceHash) || !FileHelper.ResourceExists(faceHash)) + { + faceHash = ServerConfiguration.Instance.WebsiteConfiguration.MissingIconHash; + } - string faceAlt = review.Thumb switch { - -1 => "Boo!", - 0 => "Meh.", - 1 => "Yay!", + string faceAlt = review.Thumb switch { + -1 => "Boo!", + 0 => "Meh.", + 1 => "Yay!", - _ => throw new ArgumentOutOfRangeException(), - }; + _ => throw new ArgumentOutOfRangeException(), + }; - int size = isMobile ? 50 : 100; + int size = isMobile ? 50 : 100; -
-
- @faceAlt -
-
- -

@review.Reviewer?.Username

-
- @if (review.Deleted) - { - if (review.DeletedBy == DeletedBy.LevelAuthor) - { -

- This review has been deleted by the level author. -

- } - else - { -

- This review has been deleted by a moderator. -

- } - } - else +
+
+ @faceAlt +
+
+ +

@review.Reviewer?.Username

+
+ @if (review.Deleted) + { + if (review.DeletedBy == DeletedBy.LevelAuthor) + { +

+ This review has been deleted by the level author. +

+ } + else + { +

+ This review has been deleted by a moderator. +

+ } + } + else + { + @if (review.Labels.Length > 1) + { +
+ @foreach (string reviewLabel in review.Labels) { - @if (review.Labels.Length > 1) - { -
- @foreach (string reviewLabel in review.Labels) - { -
@LabelHelper.TranslateTag(reviewLabel)
- } -
- } - @if (string.IsNullOrWhiteSpace(review.Text)) - { -

- This review contains no text. -

- } - else - { - { -

@HttpUtility.HtmlDecode(review.Text)

- } - } +
@LabelHelper.TranslateTag(reviewLabel)
}
- @if (canDelete && !review.Deleted) + } + @if (string.IsNullOrWhiteSpace(review.Text)) + { +

+ This review contains no text. +

+ } + else + { { -
- - -
- } -
- @if (i != Model.Reviews.Count - 1) - { -
- } - } -
- @if (isMobile) - { -
+
} -
\ No newline at end of file +
+ @if (i != Model.Count - 1) + { +
+ } +} +@if (isMobile) +{ +
+} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml new file mode 100644 index 000000000..c771458af --- /dev/null +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml @@ -0,0 +1,51 @@ + \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml index dbfe72356..1fea4b585 100644 --- a/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml @@ -5,7 +5,6 @@ @using LBPUnion.ProjectLighthouse.Helpers @using LBPUnion.ProjectLighthouse.Localization.StringLists @using LBPUnion.ProjectLighthouse.Servers.Website.Extensions -@using LBPUnion.ProjectLighthouse.Types.Entities.Profile @using LBPUnion.ProjectLighthouse.Types.Moderation.Cases @using LBPUnion.ProjectLighthouse.Types.Users @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.SlotPage @@ -113,51 +112,16 @@ }
- @await Html.PartialAsync("Partials/CommentsPartial", new ViewDataDictionary(ViewData.WithLang(language).WithTime(timeZone)) - { { "PageOwner", Model.Slot?.CreatorId }, }) + @await Html.PartialAsync("Partials/FramePartial", ($"/comments/slot/{Model.Slot?.SlotId}", "Comments"))
-
- @if (Model.Photos.Count != 0) - { -
- @foreach (PhotoEntity photo in Model.Photos) - { - string width = isMobile ? "sixteen" : "eight"; - bool canDelete = Model.User != null && (Model.User.IsModerator || Model.User.UserId == photo.CreatorId); -
- @await photo.ToHtml(Html, ViewData, language, timeZone, canDelete) -
- } -
- @if (isMobile) - { -
- } - } - else - { -

This level has no photos yet.

- } - -
+ @await Html.PartialAsync("Partials/FramePartial", ($"/photos/user/{Model.Slot?.SlotId}", "Photos"))
- @await Html.PartialAsync("Partials/ReviewPartial", new ViewDataDictionary(ViewData) - { - { - "isMobile", isMobile - }, - { - "CanDelete", Model.User?.IsModerator ?? false - }, - }) + @await Html.PartialAsync("Partials/FramePartial", ($"/reviews/{Model.Slot?.SlotId}", "Reviews"))
-
- @await Html.PartialAsync("Partials/LeaderboardPartial", - ViewData.WithLang(language).WithTime(timeZone).CanDelete(Model.User?.IsModerator ?? false)) -
+ @await Html.PartialAsync("Partials/FramePartial", ($"/scores/{Model.Slot?.SlotId}", "Scores"))
@@ -222,49 +186,4 @@ } } - \ No newline at end of file +@await Html.PartialAsync("Partials/SectionScriptPartial") \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml.cs index ebdd9076f..f76401540 100644 --- a/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml.cs @@ -1,10 +1,7 @@ #nullable enable -using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; -using LBPUnion.ProjectLighthouse.Types.Entities.Interaction; using LBPUnion.ProjectLighthouse.Types.Entities.Level; -using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Users; using Microsoft.AspNetCore.Mvc; @@ -14,13 +11,6 @@ namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class SlotPage : BaseLayout { - public Dictionary Comments = new(); - public List Reviews = new(); - public List Photos = new(); - public List Scores = new(); - - public bool CommentsEnabled; - public readonly bool ReviewsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelReviewsEnabled; public SlotEntity? Slot; public SlotPage(DatabaseContext database) : base(database) @@ -56,72 +46,11 @@ public async Task OnGet([FromRoute] int id) } } - if ((slot.Hidden || slot.SubLevel && (this.User == null && this.User != slot.Creator)) && !(this.User?.IsModerator ?? false)) + if ((slot.Hidden || slot.SubLevel && this.User == null && this.User != slot.Creator) && !(this.User?.IsModerator ?? false)) return this.NotFound(); this.Slot = slot; - 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.CommentsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled && this.Slot.CommentsEnabled; - if (this.CommentsEnabled) - { - this.Comments = await this.Database.Comments.Include(p => p.Poster) - .OrderByDescending(p => p.Timestamp) - .Where(c => c.TargetId == id && c.Type == CommentType.Level) - .Where(c => !blockedUsers.Contains(c.PosterUserId)) - .Include(c => c.Poster) - .Where(c => c.Poster.PermissionLevel != PermissionLevel.Banned) - .Take(50) - .ToDictionaryAsync(c => c, _ => (RatedCommentEntity?)null); - } - else - { - this.Comments = new Dictionary(); - } - - if (this.ReviewsEnabled) - { - this.Reviews = await this.Database.Reviews.Include(r => r.Reviewer) - .OrderByDescending(r => r.ThumbsUp - r.ThumbsDown) - .ThenByDescending(r => r.Timestamp) - .Where(r => r.SlotId == id) - .Where(r => !blockedUsers.Contains(r.ReviewerId)) - .Take(50) - .ToListAsync(); - } - else - { - this.Reviews = new List(); - } - - this.Photos = await this.Database.Photos.Include(p => p.Creator) - .Include(p => p.PhotoSubjects) - .ThenInclude(ps => ps.User) - .OrderByDescending(p => p.Timestamp) - .Where(r => r.SlotId == id) - .Take(10) - .ToListAsync(); - - this.Scores = await this.Database.Scores.OrderByDescending(s => s.Points) - .ThenBy(s => s.ScoreId) - .Where(s => s.SlotId == id) - .Take(10) - .ToListAsync(); - - if (this.User == null) return this.Page(); - - foreach (KeyValuePair kvp in this.Comments) - { - RatedCommentEntity? reaction = await this.Database.RatedComments.FirstOrDefaultAsync(r => r.UserId == this.User.UserId && r.CommentId == kvp.Key.CommentId); - this.Comments[kvp.Key] = reaction; - } - return this.Page(); } } diff --git a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml index beb59c81f..e3e9278ce 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml @@ -2,8 +2,6 @@ @using System.Web @using LBPUnion.ProjectLighthouse.Extensions @using LBPUnion.ProjectLighthouse.Localization.StringLists -@using LBPUnion.ProjectLighthouse.Servers.Website.Extensions -@using LBPUnion.ProjectLighthouse.Types.Entities.Level @using LBPUnion.ProjectLighthouse.Types.Moderation.Cases @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.UserPage @@ -15,8 +13,6 @@ Model.Description = Model.ProfileUser!.Biography; bool isMobile = Request.IsMobile(); - string language = Model.GetLanguage(); - string timeZone = Model.GetTimeZone(); } @if (Model.ProfileUser.IsBanned) @@ -172,26 +168,14 @@ string divLength = isMobile ? "sixteen" : "thirteen"; }
-
- +
+ @await Html.PartialAsync("Partials/FramePartial", ($"/comments/user/{Model.ProfileUser.UserId}", "Comments"))
-
- + -
- +
@@ -201,40 +185,10 @@ @if (Model.User == Model.ProfileUser) {
-
- @if (Model.HeartedSlots?.Count == 0) - { -

You haven't hearted any levels

- } - else - { -

You have hearted @(Model.HeartedSlots?.Count) levels

- } - @foreach (SlotEntity slot in Model.HeartedSlots ?? new List()) - { -
- @await slot.ToHtml(Html, ViewData, Model.User, $"~/user/{Model.ProfileUser.UserId}#hearted", language, timeZone, isMobile, true) -
- } -
+ @await Html.PartialAsync("Partials/FramePartial", ($"/slots/hearted/{Model.ProfileUser.UserId}", "Hearted Slots"))
-
- @if (Model.QueuedSlots?.Count == 0) - { -

You haven't queued any levels

- } - else - { -

There are @(Model.QueuedSlots?.Count) levels in your queue

- } - @foreach (SlotEntity slot in Model.QueuedSlots ?? new List()) - { -
- @await slot.ToHtml(Html, ViewData, Model.User, $"~/user/{Model.ProfileUser.UserId}#queued", language, timeZone, isMobile, true) -
- } -
+ @await Html.PartialAsync("Partials/FramePartial", ($"/slots/queued/{Model.ProfileUser.UserId}", "Queued Slots"))
}
@@ -286,55 +240,4 @@ } } - \ No newline at end of file +@await Html.PartialAsync("Partials/SectionScriptPartial") \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs index b4ce0190e..0b25996c7 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs @@ -1,8 +1,6 @@ #nullable enable -using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; -using LBPUnion.ProjectLighthouse.Types.Entities.Interaction; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Levels; @@ -14,20 +12,10 @@ namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages; public class UserPage : BaseLayout { - public Dictionary Comments = new(); - - public bool CommentsEnabled; - public bool IsProfileUserHearted; public bool IsProfileUserBlocked; - public List? Photos; - public List? Slots; - - public List? HeartedSlots; - public List? QueuedSlots; - public UserEntity? ProfileUser; public UserPage(DatabaseContext database) : base(database) {} @@ -59,69 +47,8 @@ public async Task OnGet([FromRoute] int userId) } } - this.Photos = await this.Database.Photos.Include(p => p.Slot) - .Include(p => p.PhotoSubjects) - .ThenInclude(ps => ps.User) - .OrderByDescending(p => p.Timestamp) - .Where(p => p.CreatorId == userId) - .Take(6) - .ToListAsync(); - - this.Slots = await this.Database.Slots.Include(p => p.Creator) - .OrderByDescending(s => s.LastUpdated) - .Where(p => p.CreatorId == userId) - .Take(10) - .ToListAsync(); - - if (this.User == this.ProfileUser) - { - this.QueuedSlots = await this.Database.QueuedLevels.Include(h => h.Slot) - .Where(q => this.User != null && q.UserId == this.User.UserId) - .OrderByDescending(q => q.QueuedLevelId) - .Select(q => q.Slot) - .Where(s => s.Type == SlotType.User) - .Take(10) - .ToListAsync(); - this.HeartedSlots = await this.Database.HeartedLevels.Include(h => h.Slot) - .Where(h => this.User != null && h.UserId == this.User.UserId) - .OrderByDescending(h => h.HeartedLevelId) - .Select(h => h.Slot) - .Where(s => s.Type == SlotType.User) - .Take(10) - .ToListAsync(); - } - - 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) - .ToDictionaryAsync(c => c, _ => (RatedCommentEntity?) null); - } - else - { - this.Comments = new Dictionary(); - } - if (this.User == null) return this.Page(); - foreach (KeyValuePair kvp in this.Comments) - { - RatedCommentEntity? reaction = await this.Database.RatedComments.Where(r => r.CommentId == kvp.Key.CommentId) - .Where(r => r.UserId == this.User.UserId) - .FirstOrDefaultAsync(); - this.Comments[kvp.Key] = reaction; - } - this.IsProfileUserHearted = await this.Database.HeartedProfiles .Where(h => h.HeartedUserId == this.ProfileUser.UserId) .Where(h => h.UserId == this.User.UserId) diff --git a/ProjectLighthouse/Database/DatabaseContext.GameTokens.cs b/ProjectLighthouse/Database/DatabaseContext.GameTokens.cs index 2ed62d8ef..da7570af1 100644 --- a/ProjectLighthouse/Database/DatabaseContext.GameTokens.cs +++ b/ProjectLighthouse/Database/DatabaseContext.GameTokens.cs @@ -2,6 +2,8 @@ using System; using System.Linq; using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Tickets; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; using LBPUnion.ProjectLighthouse.Types.Entities.Token; using Microsoft.AspNetCore.Http; @@ -11,6 +13,28 @@ namespace LBPUnion.ProjectLighthouse.Database; public partial class DatabaseContext { + public async Task AuthenticateUser(UserEntity? user, NPTicket npTicket, string userLocation) + { + if (user == null) return null; + + GameTokenEntity gameToken = new() + { + UserToken = CryptoHelper.GenerateAuthToken(), + User = user, + UserId = user.UserId, + UserLocation = userLocation, + GameVersion = npTicket.GameVersion, + Platform = npTicket.Platform, + TicketHash = npTicket.TicketHash, + // we can get away with a low expiry here since LBP will just get a new token everytime it gets 403'd + ExpiresAt = DateTime.Now + TimeSpan.FromHours(1), + }; + + this.GameTokens.Add(gameToken); + await this.SaveChangesAsync(); + + return gameToken; + } public async Task UsernameFromGameToken(GameTokenEntity? token) { diff --git a/ProjectLighthouse/Database/DatabaseContext.Utils.cs b/ProjectLighthouse/Database/DatabaseContext.User.cs similarity index 74% rename from ProjectLighthouse/Database/DatabaseContext.Utils.cs rename to ProjectLighthouse/Database/DatabaseContext.User.cs index 939f978df..545553841 100644 --- a/ProjectLighthouse/Database/DatabaseContext.Utils.cs +++ b/ProjectLighthouse/Database/DatabaseContext.User.cs @@ -1,16 +1,16 @@ -using System; +#nullable enable +using System; +using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Tickets; using LBPUnion.ProjectLighthouse.Types.Entities.Interaction; using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Moderation; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; -using LBPUnion.ProjectLighthouse.Types.Entities.Token; using Microsoft.EntityFrameworkCore; namespace LBPUnion.ProjectLighthouse.Database; @@ -30,9 +30,11 @@ public async Task CreateUser(string username, string password, strin // 16 is PSN max, 3 is PSN minimum if (!ServerStatics.IsUnitTesting || !username.StartsWith("unitTestUser")) { - if (username.Length is > 16 or < 3) throw new ArgumentException(nameof(username) + " is either too long or too short"); + if (username.Length is > 16 or < 3) + throw new ArgumentException(nameof(username) + " is either too long or too short"); - if (!this.IsUsernameValid(username)) throw new ArgumentException(nameof(username) + " does not match the username regex"); + if (!this.IsUsernameValid(username)) + throw new ArgumentException(nameof(username) + " does not match the username regex"); } UserEntity? user = await this.Users.Where(u => u.Username == username).FirstOrDefaultAsync(); @@ -58,29 +60,6 @@ public async Task UserIdFromUsername(string? username) return await this.Users.Where(u => u.Username == username).Select(u => u.UserId).FirstOrDefaultAsync(); } - public async Task AuthenticateUser(UserEntity? user, NPTicket npTicket, string userLocation) - { - if (user == null) return null; - - GameTokenEntity gameToken = new() - { - UserToken = CryptoHelper.GenerateAuthToken(), - User = user, - UserId = user.UserId, - UserLocation = userLocation, - GameVersion = npTicket.GameVersion, - Platform = npTicket.Platform, - TicketHash = npTicket.TicketHash, - // we can get away with a low expiry here since LBP will just get a new token everytime it gets 403'd - ExpiresAt = DateTime.Now + TimeSpan.FromHours(1), - }; - - this.GameTokens.Add(gameToken); - await this.SaveChangesAsync(); - - return gameToken; - } - public async Task RemoveUser(UserEntity? user) { if (user == null) return; @@ -93,13 +72,12 @@ public async Task RemoveUser(UserEntity? user) .Where(c => c.CreatorId == user.UserId || c.DismisserId == user.UserId) .ToListAsync()) { - if(modCase.DismisserId == user.UserId) - modCase.DismisserId = null; - if(modCase.CreatorId == user.UserId) - modCase.CreatorId = await SlotHelper.GetPlaceholderUserId(this); + if (modCase.DismisserId == user.UserId) modCase.DismisserId = null; + if (modCase.CreatorId == user.UserId) modCase.CreatorId = await SlotHelper.GetPlaceholderUserId(this); } - foreach (SlotEntity slot in this.Slots.Where(s => s.CreatorId == user.UserId)) await this.RemoveSlot(slot, false); + foreach (SlotEntity slot in this.Slots.Where(s => s.CreatorId == user.UserId)) + await this.RemoveSlot(slot, false); this.HeartedProfiles.RemoveRange(this.HeartedProfiles.Where(h => h.UserId == user.UserId)); this.PhotoSubjects.RemoveRange(this.PhotoSubjects.Where(s => s.UserId == user.UserId)); @@ -124,7 +102,9 @@ public async Task HeartUser(int userId, UserEntity heartedUser) { if (userId == heartedUser.UserId) return; - HeartedProfileEntity? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync(q => q.UserId == userId && q.HeartedUserId == heartedUser.UserId); + HeartedProfileEntity? heartedProfile = + await this.HeartedProfiles.FirstOrDefaultAsync(q => + q.UserId == userId && q.HeartedUserId == heartedUser.UserId); if (heartedProfile != null) return; this.HeartedProfiles.Add(new HeartedProfileEntity @@ -138,7 +118,9 @@ public async Task HeartUser(int userId, UserEntity heartedUser) public async Task UnheartUser(int userId, UserEntity heartedUser) { - HeartedProfileEntity? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync(q => q.UserId == userId && q.HeartedUserId == heartedUser.UserId); + HeartedProfileEntity? heartedProfile = + await this.HeartedProfiles.FirstOrDefaultAsync(q => + q.UserId == userId && q.HeartedUserId == heartedUser.UserId); if (heartedProfile != null) this.HeartedProfiles.Remove(heartedProfile); await this.SaveChangesAsync(); @@ -168,11 +150,17 @@ public async Task UnblockUser(int userId, UserEntity blockedUser) await this.BlockedProfiles.RemoveWhere(bp => bp.BlockedUserId == blockedUser.UserId && bp.UserId == userId); } + public Task> GetBlockedUsers(int? userId) + { + return userId == null + ? Task.FromResult(new List()) + : this.BlockedProfiles.Where(b => b.UserId == userId).Select(b => b.BlockedUserId).ToListAsync(); + } + 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); } - } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Entities/Level/ReviewEntity.cs b/ProjectLighthouse/Types/Entities/Level/ReviewEntity.cs index 59091953c..a20b23acf 100644 --- a/ProjectLighthouse/Types/Entities/Level/ReviewEntity.cs +++ b/ProjectLighthouse/Types/Entities/Level/ReviewEntity.cs @@ -1,4 +1,3 @@ -#nullable enable using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -15,12 +14,12 @@ public class ReviewEntity public int ReviewerId { get; set; } [ForeignKey(nameof(ReviewerId))] - public UserEntity? Reviewer { get; set; } + public UserEntity Reviewer { get; set; } public int SlotId { get; set; } [ForeignKey(nameof(SlotId))] - public SlotEntity? Slot { get; set; } + public SlotEntity Slot { get; set; } public long Timestamp { get; set; } diff --git a/ProjectLighthouse/Types/Entities/Profile/CommentEntity.cs b/ProjectLighthouse/Types/Entities/Profile/CommentEntity.cs index ec2cd6405..14bb50769 100644 --- a/ProjectLighthouse/Types/Entities/Profile/CommentEntity.cs +++ b/ProjectLighthouse/Types/Entities/Profile/CommentEntity.cs @@ -50,9 +50,11 @@ public string GetCommentMessage(DatabaseContext database) return "This comment has been deleted by the author."; } - UserEntity deletedBy = database.Users.FirstOrDefault(u => u.Username == this.DeletedBy); + int deletedById = database.Users.Where(u => u.Username == this.DeletedBy) + .Select(u => u.UserId) + .FirstOrDefault(); - if (deletedBy != null && deletedBy.UserId == this.TargetId) + if (deletedById != 0 && deletedById == this.TargetId) { return "This comment has been deleted by the player."; } From 9761268e13694c6b09d7abd4965cd45d8afeba91 Mon Sep 17 00:00:00 2001 From: Slendy Date: Fri, 7 Jul 2023 21:03:56 -0500 Subject: [PATCH 05/16] Fix frame pagination --- .../Pages/Frames/PaginatedFrame.cshtml.cs | 4 ++-- ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs index 9435e69ba..10908a737 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs @@ -12,14 +12,14 @@ protected PaginatedFrame(DatabaseContext database) : base(database) public int CurrentPage { get; set; } public int TotalItems { get; set; } public int ItemsPerPage { get; set; } - public int TotalPages => Math.Max(1, this.TotalItems / this.ItemsPerPage); + public int TotalPages => Math.Max(1, (int)Math.Ceiling(this.TotalItems / (float)this.ItemsPerPage)); public PaginationData PageData => new() { MaxElements = this.ItemsPerPage, PageSize = this.ItemsPerPage, - PageStart = this.CurrentPage, + PageStart = this.CurrentPage * this.ItemsPerPage, TotalElements = this.TotalItems, }; diff --git a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs index 0b25996c7..090d50c59 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml.cs @@ -1,9 +1,7 @@ #nullable enable using LBPUnion.ProjectLighthouse.Database; using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; -using LBPUnion.ProjectLighthouse.Types.Entities.Level; using LBPUnion.ProjectLighthouse.Types.Entities.Profile; -using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Users; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; From f451d14a7be76864cd2e8c50fa14e50893fdbcf5 Mon Sep 17 00:00:00 2001 From: Slendy Date: Fri, 7 Jul 2023 21:06:46 -0500 Subject: [PATCH 06/16] Make initially unselected iframes not visible --- ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml | 6 +++--- ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml index 1fea4b585..056a81c63 100644 --- a/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml @@ -114,13 +114,13 @@
@await Html.PartialAsync("Partials/FramePartial", ($"/comments/slot/{Model.Slot?.SlotId}", "Comments"))
-
+ -
+ -
+
diff --git a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml index e3e9278ce..723cdc7d5 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml @@ -177,17 +177,17 @@ -
+ @if (Model.User == Model.ProfileUser) { -
+ -
+ } From 80a4b8659066fecd720c4df1a50a29934a597c86 Mon Sep 17 00:00:00 2001 From: Slendy Date: Fri, 7 Jul 2023 21:50:39 -0500 Subject: [PATCH 07/16] Fix frame sizing on page change --- .../Pages/Partials/FramePartial.cshtml | 2 +- .../Pages/Partials/SectionScriptPartial.cshtml | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml index b3dc53cdb..01c81405e 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml @@ -5,5 +5,5 @@ style="width: 100%; height: 100%; border: 0; overflow: hidden" scrolling="no" seamless="seamless" - onload="this.style.height=((this.contentWindow.document.body.scrollHeight) + 'px')"> + onload="this.style.height=(this.contentWindow.document.querySelector('div').scrollHeight + 'px')"> \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml index c771458af..93afacb70 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml @@ -20,10 +20,14 @@ function setVisible(e){ } // unhide content eTarget.style.display = ""; + let frame = eTarget.children[0] || undefined; // set iframe height - if (frame !== undefined && frame.nodeName === "IFRAME"){ - frame.style.height = (frame.contentWindow.document.body.scrollHeight+2) + "px"; + if (frame?.nodeName === "IFRAME"){ + let mainDiv = frame.contentWindow.document.querySelector('div'); + if (mainDiv != null){ + frame.style.height = mainDiv.scrollHeight + "px"; + } } e.classList.add("active"); From 2e233a81155a8237ce82773662991fb00a64ddd3 Mon Sep 17 00:00:00 2001 From: Slendy Date: Fri, 7 Jul 2023 21:56:06 -0500 Subject: [PATCH 08/16] Fix item pagination and add first and last page buttons --- .../Pages/Frames/PaginatedFrame.cshtml | 12 ++++++++++-- .../Pages/Frames/PaginatedFrame.cshtml.cs | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml index 40dbc51be..8e2ac6746 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml @@ -18,14 +18,22 @@ @RenderBody()
+ @{ + string? url = Url.RouteUrl(ViewContext.RouteData.Values); + } + ] @if (Model.CurrentPage > 1) { - Previous Page + First Page + | + Previous Page } Page @Model.CurrentPage / @Model.TotalPages @if (Model.CurrentPage < Model.TotalPages) { - Next Page + Next Page + | + Last Page }
diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs index 10908a737..f920ec456 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs @@ -19,7 +19,7 @@ protected PaginatedFrame(DatabaseContext database) : base(database) { MaxElements = this.ItemsPerPage, PageSize = this.ItemsPerPage, - PageStart = this.CurrentPage * this.ItemsPerPage, + PageStart = (this.CurrentPage - 1) * this.ItemsPerPage, TotalElements = this.TotalItems, }; From 5e48b87c3e495ead047b053d9d790724b726bc85 Mon Sep 17 00:00:00 2001 From: Slendy Date: Fri, 7 Jul 2023 21:57:39 -0500 Subject: [PATCH 09/16] Remove extra character in PaginatedFrame --- .../Pages/Frames/PaginatedFrame.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml index 8e2ac6746..37aae1916 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml @@ -21,7 +21,7 @@ @{ string? url = Url.RouteUrl(ViewContext.RouteData.Values); } - ] + @if (Model.CurrentPage > 1) { First Page From 3287b978cb55bc3c1d61c1d95c5d59461dfb5c39 Mon Sep 17 00:00:00 2001 From: Slendy Date: Fri, 7 Jul 2023 22:05:11 -0500 Subject: [PATCH 10/16] Allow fragments without "lh-" prefix --- .../Pages/Partials/FramePartial.cshtml | 2 +- .../Pages/Partials/SectionScriptPartial.cshtml | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml index 01c81405e..c3acf9010 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml @@ -5,5 +5,5 @@ style="width: 100%; height: 100%; border: 0; overflow: hidden" scrolling="no" seamless="seamless" - onload="this.style.height=(this.contentWindow.document.querySelector('div').scrollHeight + 'px')"> + onload="this.style.height=(this.contentWindow.document.querySelector('body').scrollHeight + 'px')"> \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml index 93afacb70..9c77ecde3 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml @@ -1,9 +1,11 @@ From a863d74e4e06cdd2d9b5fb5aff06cc5a60270a54 Mon Sep 17 00:00:00 2001 From: Slendy Date: Fri, 7 Jul 2023 23:06:55 -0500 Subject: [PATCH 13/16] Fix page numbers when there are no elements --- .../Pages/Frames/CommentFrame.cshtml.cs | 1 + .../Pages/Frames/ReviewFrame.cshtml.cs | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs index a6faff947..191e2465f 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs @@ -88,6 +88,7 @@ public async Task OnGet([FromQuery] int page, string type, int id } else { + this.ClampPage(); this.Comments = new Dictionary(); } diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/ReviewFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/ReviewFrame.cshtml.cs index 37f7c6f2d..2fa5537a1 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/ReviewFrame.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/ReviewFrame.cshtml.cs @@ -26,7 +26,11 @@ public async Task OnGet([FromQuery] int page, int slotId) SlotEntity? slot = await this.Database.Slots.FindAsync(slotId); if (slot == null) return this.BadRequest(); - if (!this.ReviewsEnabled) return this.Page(); + if (!this.ReviewsEnabled) + { + this.ClampPage(); + return this.Page(); + } List blockedUsers = await this.Database.GetBlockedUsers(this.User?.UserId); From 12f25e4c39951f90a49dfab340907deec9e0463f Mon Sep 17 00:00:00 2001 From: Slendy Date: Tue, 18 Jul 2023 02:31:40 -0500 Subject: [PATCH 14/16] Add placeholder for frames and fix height calculation Also fixes the off-by-one error in pagination and fixes response caching for game assets --- .../Pages/Frames/CommentFrame.cshtml.cs | 24 +++--- .../Pages/Frames/PaginatedFrame.cshtml | 55 ++++++------- .../Pages/Frames/PaginatedFrame.cshtml.cs | 2 +- .../Pages/Layouts/BaseFrame.cshtml | 16 +++- .../Pages/Layouts/BaseFrame.cshtml.cs | 56 +------------ .../Pages/Layouts/BaseLayout.cshtml | 4 +- .../Pages/Partials/FramePartial.cshtml | 5 +- .../Partials/Links/UserLinkPartial.cshtml | 2 +- .../Pages/Partials/PhotoPartial.cshtml | 14 +++- .../Pages/Partials/ReviewPartial.cshtml | 2 +- .../Partials/SectionScriptPartial.cshtml | 78 ++++++++++++++----- .../Pages/Partials/SlotCardPartial.cshtml | 8 +- .../Pages/SlotPage.cshtml | 43 +++++----- .../Pages/UserPage.cshtml | 33 ++++---- .../Startup/WebsiteStartup.cs | 2 + ProjectLighthouse/StaticFiles/css/styles.css | 9 +++ .../Types/Serialization/GamePhotoSubject.cs | 4 +- 17 files changed, 188 insertions(+), 169 deletions(-) diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs index 191e2465f..1ccc1204f 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/CommentFrame.cshtml.cs @@ -41,20 +41,20 @@ public async Task OnGet([FromQuery] int page, string type, int id { case CommentType.Level: { - this.CommentsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled; SlotEntity? slot = await this.Database.Slots.FindAsync(id); if (slot == null) return this.BadRequest(); this.PageOwner = slot.CreatorId; - this.CommentsEnabled &= slot.CommentsEnabled; + this.CommentsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled && + slot.CommentsEnabled; break; } case CommentType.Profile: { - this.CommentsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled; UserEntity? user = await this.Database.Users.FindAsync(id); if (user == null) return this.BadRequest(); this.PageOwner = user.UserId; - this.CommentsEnabled &= user.CommentsEnabled; + this.CommentsEnabled = ServerConfiguration.Instance.UserGeneratedContentLimits.ProfileCommentsEnabled && + user.CommentsEnabled; break; } default: return this.BadRequest(); @@ -75,16 +75,12 @@ public async Task OnGet([FromQuery] int page, string type, int id this.Comments = await commentQuery.OrderByDescending(c => c.Timestamp) .ApplyPagination(this.PageData) - .ToDictionaryAsync(c => c, _ => (RatedCommentEntity?)null); - - if (this.User == null) return this.Page(); - - foreach (KeyValuePair kvp in this.Comments) - { - RatedCommentEntity? reaction = await this.Database.RatedComments.FirstOrDefaultAsync(r => - r.UserId == this.User.UserId && r.CommentId == kvp.Key.CommentId); - this.Comments[kvp.Key] = reaction; - } + .Select(c => new + { + Comment = c, + YourRating = this.Database.RatedComments.FirstOrDefault(r => r.CommentId == c.CommentId), + }) + .ToDictionaryAsync(c => c.Comment, c => c.YourRating); } else { diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml index 37aae1916..13c50c133 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml @@ -1,41 +1,30 @@ -@using LBPUnion.ProjectLighthouse.Extensions @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Frames.PaginatedFrame - - @{ - Model.IsMobile = Model.Request.IsMobile(); + Layout = "Layouts/BaseFrame"; } - - - @Model.Title - - - - -
- @RenderBody() +@RenderBody() +@section Pagination +{
- @{ - string? url = Url.RouteUrl(ViewContext.RouteData.Values); - } - - @if (Model.CurrentPage > 1) - { - First Page - | - Previous Page - } - Page @Model.CurrentPage / @Model.TotalPages - @if (Model.CurrentPage < Model.TotalPages) - { - Next Page - | - Last Page - } + @{ + string? url = Url.RouteUrl(ViewContext.RouteData.Values); + } + + @if (Model.CurrentPage > 1) + { + First Page + | + Previous Page + } + Page @Model.CurrentPage / @Model.TotalPages + @if (Model.CurrentPage < Model.TotalPages) + { + Next Page + | + Last Page + }
-
- - \ No newline at end of file +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs index f920ec456..0c0c553f9 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Frames/PaginatedFrame.cshtml.cs @@ -19,7 +19,7 @@ protected PaginatedFrame(DatabaseContext database) : base(database) { MaxElements = this.ItemsPerPage, PageSize = this.ItemsPerPage, - PageStart = (this.CurrentPage - 1) * this.ItemsPerPage, + PageStart = (this.CurrentPage - 1) * this.ItemsPerPage + 1, TotalElements = this.TotalItems, }; diff --git a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml index 2ade2eb94..98441ee15 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml @@ -1,7 +1,7 @@ @using LBPUnion.ProjectLighthouse.Extensions @model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts.BaseFrame - + @{ Model.IsMobile = Model.Request.IsMobile(); @@ -12,10 +12,24 @@ @Model.Title +
@RenderBody() + + @await RenderSectionAsync("Pagination", false)
\ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml.cs index 3cc953ca3..e0bd18fa7 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml.cs +++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseFrame.cshtml.cs @@ -1,59 +1,9 @@ -using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Database; -using LBPUnion.ProjectLighthouse.Localization; -using LBPUnion.ProjectLighthouse.Types.Entities.Profile; -using Microsoft.AspNetCore.Localization; -using Microsoft.AspNetCore.Mvc.RazorPages; namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts; -public class BaseFrame : PageModel +public class BaseFrame : BaseLayout { - public readonly DatabaseContext Database; - - public BaseFrame(DatabaseContext database) - { - this.Database = database; - } - - public string Title = string.Empty; - public bool IsMobile; - - private UserEntity? user; - public new UserEntity? User - { - get - { - if (this.user != null) return this.user; - - return this.user = this.Database.UserFromWebRequest(this.Request); - } - set => this.user = value; - } - - private string? language; - private string? timeZone; - - public string GetLanguage() - { - if (ServerStatics.IsUnitTesting) return LocalizationManager.DefaultLang; - if (this.language != null) return this.language; - - if (this.User != null) return this.language = this.User.Language; - - IRequestCultureFeature? requestCulture = this.Request.HttpContext.Features.Get(); - if (requestCulture == null) return this.language = LocalizationManager.DefaultLang; - - return this.language = requestCulture.RequestCulture.UICulture.Name; - } - - public string GetTimeZone() - { - if (ServerStatics.IsUnitTesting) return TimeZoneInfo.Local.Id; - if (this.timeZone != null) return this.timeZone; - - string userTimeZone = this.User?.TimeZone ?? TimeZoneInfo.Local.Id; - - return this.timeZone = userTimeZone; - } + public BaseFrame(DatabaseContext database) : base(database) + { } } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml index b988b5f60..b910886e6 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml @@ -69,9 +69,7 @@ if (window.self !== window.top) { window.stop() let locationHref = window.location.href; - location.replace(document.referrer); - document.documentElement.style.display = "none"; - + history.replaceState({}, '', window.top.location); window.top.location.replace(locationHref); } diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml index c3acf9010..dc984a676 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/FramePartial.cshtml @@ -2,8 +2,7 @@ \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/Links/UserLinkPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/Links/UserLinkPartial.cshtml index 30a21be4f..33cc138af 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/Links/UserLinkPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/Links/UserLinkPartial.cshtml @@ -12,7 +12,7 @@ string userStatus = includeStatus ? Model.GetStatus(Database).ToTranslatedString(language, timeZone) : ""; } - + @if (Model.IsModerator) diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/PhotoPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/PhotoPartial.cshtml index 70b98e64a..8691f758a 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/PhotoPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/PhotoPartial.cshtml @@ -28,7 +28,7 @@ } } - @(Model.Slot.IsAdventurePlanet ? "on an adventure in" : "in level") - @HttpUtility.HtmlDecode(Model.Slot.Name) + + @HttpUtility.HtmlDecode(Model.Slot.Name) + break; case SlotType.Developer: @@ -93,11 +95,14 @@ GamePhotoSubject[] subjects = Model.PhotoSubjects.Select(GamePhotoSubject.CreateFromEntity).ToArray(); foreach (GamePhotoSubject subject in subjects) { + subject.Bounds ??= ""; subject.Username = Model.PhotoSubjects.Where(ps => ps.UserId == subject.UserId).Select(ps => ps.User.Username).First(); } } - + +} \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/ReviewPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/ReviewPartial.cshtml index ee13af03d..10dfe61fc 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/ReviewPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/ReviewPartial.cshtml @@ -42,7 +42,7 @@ @faceAlt
- +

@review.Reviewer?.Username

@if (review.Deleted) diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml index 9c77ecde3..37ea09799 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/SectionScriptPartial.cshtml @@ -7,51 +7,91 @@ if (selectedId.startsWith("#")) selectedId = selectedId.substring(1); if (!selectedId.startsWith("lh-")) selectedId = "lh-" + selectedId; let selectedElement = document.getElementById(selectedId); +const loadedContent = {}; + // id = lh-sidebar element function setVisible(e){ let eTarget = document.getElementById(e.target); if (!e || !eTarget) return; - // make all active elements not active + // Make all sidebar elements inactive for (let active of document.getElementsByClassName("active")) { active.classList.remove("active"); } - // hide all content divs + + // Hide all content containers for (let i = 0; i < contentElements.length; i++){ + // Don't try and hide elements that may still be loading + if (contentElements[i].classList.contains("hidden")) continue; + contentElements[i].style.display = "none"; } - // unhide content - eTarget.style.display = ""; - - let frame = eTarget.children[0] || undefined; - // set iframe height - if (frame?.nodeName === "IFRAME"){ - let mainDiv = frame.contentWindow.document.querySelector('body'); - if (mainDiv != null){ - frame.style.height = mainDiv.scrollHeight + "px"; - } + + // Show placeholder if a frame hasn't finished loading yet + if (loadedContent[eTarget.id]){ + eTarget.classList.remove("hidden"); + document.getElementById("lh-placeholder").style.display = "none"; + } else if (document.getElementById("lh-placeholder").style.display === "none") { + document.getElementById("lh-placeholder").style.display = ""; } + + // Show the element + if (!eTarget.classList.contains("hidden")) eTarget.style.display = ""; + + let frame = eTarget.querySelector("iframe"); + // Fail safe if somehow the frame's height is 0 + // (like if it's height was measured when it was set to not display) + if (frame !== null && !eTarget.classList.contains("hidden") && frame.getBoundingClientRect().height === 0){ + frame.style.height = getHeight(frame) + "px"; + } + + window.location.hash = eTarget.id.replace("lh-", ""); e.classList.add("active"); } -sidebarElements.forEach(el => { - if (el.classList.contains("active")){ - setVisible(el); +function getHeight(frame) { + return frame.contentDocument.body.querySelector("div").scrollHeight; +} + +// Setup pre-load listeners for iFrames +for (let element of contentElements){ + let frame = element.querySelector("iframe"); + if (frame !== null){ + frame.contentWindow.addEventListener("load", () => { + let contentItem = frame.parentElement; + + frame.style.height = getHeight(frame) + 'px'; + frame.parentElement.classList.remove('hidden'); + frame.parentElement.style.display = 'none'; + + loadedContent[contentItem.id] = true; + + setVisible(document.querySelector(".lh-sidebar.active")); + }); + } else { + loadedContent[element.id] = true; } +} + +// Setup click listeners for sidebar +sidebarElements.forEach(el => { el.addEventListener('click', event => { if (!event.target.target) return; + + if (event.target.classList.contains("active")) return; setVisible(event.target) }) }) -// set the active content window based on url + +// Set initial active element if (selectedElement != null) { while (selectedElement != null && !selectedElement.classList.contains("lh-content")){ selectedElement = selectedElement.parentElement; } - - let sidebarEle = document.querySelector("[target=" + selectedElement.id + "]") - setVisible(sidebarEle); + setVisible(document.querySelector("[target=" + selectedElement.id + "]")); +} else { + setVisible(document.querySelector(".lh-sidebar.active")); } \ No newline at end of file diff --git a/ProjectLighthouse.Servers.Website/Pages/Partials/SlotCardPartial.cshtml b/ProjectLighthouse.Servers.Website/Pages/Partials/SlotCardPartial.cshtml index 43ac34237..e74e1f535 100644 --- a/ProjectLighthouse.Servers.Website/Pages/Partials/SlotCardPartial.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/Partials/SlotCardPartial.cshtml @@ -53,7 +53,7 @@ @if (showLink) {

- @slotName + @slotName

} else @@ -68,7 +68,7 @@ @if (showLink) {

- @slotName + @slotName

} else @@ -114,12 +114,12 @@
@if (user != null && !mini && (user.IsModerator || Model.CreatorId == user.UserId)) { - + } - @if (user != null && !mini && (user.UserId != Model.CreatorId)) + @if (user != null && !mini && user.UserId != Model.CreatorId) { diff --git a/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml index 69470994b..2640f113f 100644 --- a/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/SlotPage.cshtml @@ -100,15 +100,11 @@
}
- +
@{ - string outerDiv = isMobile ? - "horizontal-scroll" : - "three wide column"; - string innerDiv = isMobile ? - "ui top attached tabular menu horizontal-scroll" : - "ui vertical fluid tabular menu"; + string outerDiv = isMobile ? "horizontal-scroll" : "three wide column"; + string innerDiv = isMobile ? "ui top attached tabular menu horizontal-scroll" : "ui vertical fluid tabular menu"; }
@@ -131,25 +127,36 @@ string divLength = isMobile ? "sixteen" : "thirteen"; } } - + @if (Model.Slot!.CommentsEnabled) { diff --git a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml index 40d9290af..456ced6e2 100644 --- a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml +++ b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml @@ -132,12 +132,8 @@
@{ - string outerDiv = isMobile ? - "horizontal-scroll" : - "three wide column"; - string innerDiv = isMobile ? - "ui top attached tabular menu horizontal-scroll" : - "ui vertical fluid tabular menu"; + string outerDiv = isMobile ? "horizontal-scroll" : "three wide column"; + string innerDiv = isMobile ? "ui top attached tabular menu horizontal-scroll" : "ui vertical fluid tabular menu"; }
@@ -168,26 +164,37 @@ string divLength = isMobile ? "sixteen" : "thirteen"; }
-
+
+ @for (int i = 0; i < 3; i++) + { +
+ @for (int j = 0; j < 5; j++) + { +
+ } +
+ } +
+ -