mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-07-30 00:38:38 +00:00
Fixes and improvements from Operation Purge (#267)
* Fixes and improvements from Operation Purge * Limit level name and description length * Implement suggestions from code review Co-authored-by: jvyden <jvyden@jvyden.xyz>
This commit is contained in:
parent
5b98d00d05
commit
8ef8c204a1
19 changed files with 243 additions and 93 deletions
|
@ -5,6 +5,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
|
@ -78,16 +79,18 @@ public class CommentController : ControllerBase
|
|||
[HttpPost("postComment/user/{slotId:int}")]
|
||||
public async Task<IActionResult> PostComment(string? username, int? slotId)
|
||||
{
|
||||
User? poster = await this.database.UserFromGameRequest(this.Request);
|
||||
if (poster == null) return this.StatusCode(403, "");
|
||||
|
||||
this.Request.Body.Position = 0;
|
||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
XmlSerializer serializer = new(typeof(Comment));
|
||||
Comment? comment = (Comment?)serializer.Deserialize(new StringReader(bodyString));
|
||||
|
||||
CommentType type = (slotId.GetValueOrDefault() == 0 ? CommentType.Profile : CommentType.Level);
|
||||
SanitizationHelper.SanitizeStringsInClass(comment);
|
||||
|
||||
User? poster = await this.database.UserFromGameRequest(this.Request);
|
||||
if (poster == null) return this.StatusCode(403, "");
|
||||
CommentType type = (slotId.GetValueOrDefault() == 0 ? CommentType.Profile : CommentType.Level);
|
||||
|
||||
if (comment == null) return this.BadRequest();
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ public class FriendsController : ControllerBase
|
|||
NPData? npData = (NPData?)serializer.Deserialize(new StringReader(bodyString));
|
||||
if (npData == null) return this.BadRequest();
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(npData);
|
||||
|
||||
List<User> friends = new();
|
||||
foreach (string friendName in npData.Friends)
|
||||
{
|
||||
|
|
|
@ -36,6 +36,8 @@ public class ReportController : ControllerBase
|
|||
|
||||
if (report == null) return this.BadRequest();
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(report);
|
||||
|
||||
report.Bounds = JsonSerializer.Serialize(report.XmlBounds.Rect, typeof(Rectangle));
|
||||
report.Players = JsonSerializer.Serialize(report.XmlPlayers, typeof(ReportPlayer[]));
|
||||
report.Timestamp = TimeHelper.UnixTimeMilliseconds();
|
||||
|
|
|
@ -44,6 +44,8 @@ public class PhotosController : ControllerBase
|
|||
Photo? photo = (Photo?)serializer.Deserialize(new StringReader(bodyString));
|
||||
if (photo == null) return this.BadRequest();
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(photo);
|
||||
|
||||
foreach (Photo p in this.database.Photos.Where(p => p.CreatorId == user.UserId))
|
||||
{
|
||||
if (p.LargeHash == photo.LargeHash) return this.Ok(); // photo already uplaoded
|
||||
|
|
|
@ -30,7 +30,7 @@ public class ResourcesController : ControllerBase
|
|||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
XmlSerializer serializer = new(typeof(ResourceList));
|
||||
ResourceList resourceList = (ResourceList)serializer.Deserialize(new StringReader(bodyString));
|
||||
ResourceList? resourceList = (ResourceList?)serializer.Deserialize(new StringReader(bodyString));
|
||||
|
||||
if (resourceList == null) return this.BadRequest();
|
||||
|
||||
|
|
|
@ -85,7 +85,14 @@ public class PublishController : ControllerBase
|
|||
User user = userAndToken.Value.Item1;
|
||||
GameToken gameToken = userAndToken.Value.Item2;
|
||||
Slot? slot = await this.getSlotFromBody();
|
||||
if (slot?.Location == null) return this.BadRequest();
|
||||
|
||||
if (slot == null) return this.BadRequest();
|
||||
|
||||
if (slot.Location == null) return this.BadRequest();
|
||||
|
||||
if (slot.Description.Length > 200) return this.BadRequest();
|
||||
|
||||
if (slot.Name.Length > 100) return this.BadRequest();
|
||||
|
||||
foreach (string resource in slot.Resources)
|
||||
{
|
||||
|
@ -219,6 +226,8 @@ public class PublishController : ControllerBase
|
|||
|
||||
XmlSerializer serializer = new(typeof(Slot));
|
||||
Slot? slot = (Slot?)serializer.Deserialize(new StringReader(bodyString));
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(slot);
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
|
|
@ -41,10 +41,12 @@ public class ReviewController : ControllerBase
|
|||
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
|
||||
if (ratedLevel == null)
|
||||
{
|
||||
ratedLevel = new RatedLevel();
|
||||
ratedLevel.SlotId = slotId;
|
||||
ratedLevel.UserId = user.UserId;
|
||||
ratedLevel.Rating = 0;
|
||||
ratedLevel = new RatedLevel
|
||||
{
|
||||
SlotId = slotId,
|
||||
UserId = user.UserId,
|
||||
Rating = 0,
|
||||
};
|
||||
this.database.RatedLevels.Add(ratedLevel);
|
||||
}
|
||||
|
||||
|
@ -68,10 +70,12 @@ public class ReviewController : ControllerBase
|
|||
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
|
||||
if (ratedLevel == null)
|
||||
{
|
||||
ratedLevel = new RatedLevel();
|
||||
ratedLevel.SlotId = slotId;
|
||||
ratedLevel.UserId = user.UserId;
|
||||
ratedLevel.RatingLBP1 = 0;
|
||||
ratedLevel = new RatedLevel
|
||||
{
|
||||
SlotId = slotId,
|
||||
UserId = user.UserId,
|
||||
RatingLBP1 = 0,
|
||||
};
|
||||
this.database.RatedLevels.Add(ratedLevel);
|
||||
}
|
||||
|
||||
|
@ -91,18 +95,23 @@ public class ReviewController : ControllerBase
|
|||
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId);
|
||||
Review? newReview = await this.getReviewFromBody();
|
||||
if (newReview == null) return this.BadRequest();
|
||||
|
||||
if (newReview.Text.Length > 100) return this.BadRequest();
|
||||
|
||||
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId);
|
||||
|
||||
if (review == null)
|
||||
{
|
||||
review = new Review();
|
||||
review.SlotId = slotId;
|
||||
review.ReviewerId = user.UserId;
|
||||
review.DeletedBy = DeletedBy.None;
|
||||
review.ThumbsUp = 0;
|
||||
review.ThumbsDown = 0;
|
||||
review = new Review
|
||||
{
|
||||
SlotId = slotId,
|
||||
ReviewerId = user.UserId,
|
||||
DeletedBy = DeletedBy.None,
|
||||
ThumbsUp = 0,
|
||||
ThumbsDown = 0,
|
||||
};
|
||||
this.database.Reviews.Add(review);
|
||||
}
|
||||
review.Thumb = newReview.Thumb;
|
||||
|
@ -115,10 +124,12 @@ public class ReviewController : ControllerBase
|
|||
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
|
||||
if (ratedLevel == null)
|
||||
{
|
||||
ratedLevel = new RatedLevel();
|
||||
ratedLevel.SlotId = slotId;
|
||||
ratedLevel.UserId = user.UserId;
|
||||
ratedLevel.RatingLBP1 = 0;
|
||||
ratedLevel = new RatedLevel
|
||||
{
|
||||
SlotId = slotId,
|
||||
UserId = user.UserId,
|
||||
RatingLBP1 = 0,
|
||||
};
|
||||
this.database.RatedLevels.Add(ratedLevel);
|
||||
}
|
||||
|
||||
|
@ -149,12 +160,14 @@ public class ReviewController : ControllerBase
|
|||
.Where(r => r.SlotId == slotId)
|
||||
.Include(r => r.Reviewer)
|
||||
.Include(r => r.Slot)
|
||||
.OrderByDescending(r => r.ThumbsUp)
|
||||
.OrderByDescending(r => r.ThumbsUp - r.ThumbsDown)
|
||||
.ThenByDescending(r => r.Timestamp)
|
||||
.Skip(pageStart - 1)
|
||||
.Take(pageSize);
|
||||
|
||||
string inner = reviews.ToList()
|
||||
List<Review?> reviewList = reviews.ToList();
|
||||
|
||||
string inner = reviewList
|
||||
.Aggregate
|
||||
(
|
||||
string.Empty,
|
||||
|
@ -162,10 +175,10 @@ public class ReviewController : ControllerBase
|
|||
{
|
||||
if (review == null) return current;
|
||||
|
||||
return current + review.Serialize();
|
||||
RatedReview? yourThumb = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId);
|
||||
return current + review.Serialize(null, yourThumb);
|
||||
}
|
||||
);
|
||||
|
||||
string response = LbpSerializer.TaggedStringElement
|
||||
(
|
||||
"reviews",
|
||||
|
@ -176,7 +189,7 @@ public class ReviewController : ControllerBase
|
|||
"hint_start", pageStart + pageSize
|
||||
},
|
||||
{
|
||||
"hint", pageStart // not sure
|
||||
"hint", reviewList.LastOrDefault()!.Timestamp // not sure
|
||||
},
|
||||
}
|
||||
);
|
||||
|
@ -197,22 +210,24 @@ public class ReviewController : ControllerBase
|
|||
GameVersion gameVersion = gameToken.GameVersion;
|
||||
|
||||
IEnumerable<Review?> reviews = this.database.Reviews.ByGameVersion(gameVersion, true)
|
||||
.Where(r => r.Reviewer.Username == username)
|
||||
.Include(r => r.Reviewer)
|
||||
.Include(r => r.Slot)
|
||||
.Where(r => r.Reviewer!.Username == username)
|
||||
.OrderByDescending(r => r.Timestamp)
|
||||
.Skip(pageStart - 1)
|
||||
.Take(pageSize)
|
||||
.AsEnumerable();
|
||||
.Take(pageSize);
|
||||
|
||||
string inner = reviews.Aggregate
|
||||
List<Review?> reviewList = reviews.ToList();
|
||||
|
||||
string inner = reviewList.Aggregate
|
||||
(
|
||||
string.Empty,
|
||||
(current, review) =>
|
||||
{
|
||||
//RatedLevel? ratedLevel = this.database.RatedLevels.FirstOrDefault(r => r.SlotId == review.SlotId && r.UserId == user.UserId);
|
||||
//RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId);
|
||||
return current + review.Serialize( /*, ratedReview*/);
|
||||
if (review == null) return current;
|
||||
|
||||
RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId);
|
||||
return current + review.Serialize(null, ratedReview);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -226,7 +241,7 @@ public class ReviewController : ControllerBase
|
|||
"hint_start", pageStart
|
||||
},
|
||||
{
|
||||
"hint", reviews.Last().Timestamp // Seems to be the timestamp of oldest
|
||||
"hint", reviewList.LastOrDefault()!.Timestamp // Seems to be the timestamp of oldest
|
||||
},
|
||||
}
|
||||
);
|
||||
|
@ -249,25 +264,40 @@ public class ReviewController : ControllerBase
|
|||
RatedReview? ratedReview = await this.database.RatedReviews.FirstOrDefaultAsync(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId);
|
||||
if (ratedReview == null)
|
||||
{
|
||||
ratedReview = new RatedReview();
|
||||
ratedReview.ReviewId = review.ReviewId;
|
||||
ratedReview.UserId = user.UserId;
|
||||
ratedReview.Thumb = 0;
|
||||
ratedReview = new RatedReview
|
||||
{
|
||||
ReviewId = review.ReviewId,
|
||||
UserId = user.UserId,
|
||||
Thumb = 0,
|
||||
};
|
||||
this.database.RatedReviews.Add(ratedReview);
|
||||
await this.database.SaveChangesAsync();
|
||||
}
|
||||
|
||||
int oldThumb = ratedReview.Thumb;
|
||||
ratedReview.Thumb = Math.Max(Math.Min(1, rating), -1);
|
||||
int oldRating = ratedReview.Thumb;
|
||||
ratedReview.Thumb = Math.Clamp(rating, -1, 1);
|
||||
if (oldRating == ratedReview.Thumb) return this.Ok();
|
||||
|
||||
if (oldThumb != ratedReview.Thumb)
|
||||
// if the user's rating changed then we recount the review's ratings to ensure accuracy
|
||||
List<RatedReview> reactions = await this.database.RatedReviews.Where(r => r.ReviewId == review.ReviewId).ToListAsync();
|
||||
int yay = 0;
|
||||
int boo = 0;
|
||||
foreach (RatedReview r in reactions)
|
||||
{
|
||||
if (oldThumb == -1) review.ThumbsDown--;
|
||||
else if (oldThumb == 1) review.ThumbsUp--;
|
||||
|
||||
if (ratedReview.Thumb == -1) review.ThumbsDown++;
|
||||
else if (ratedReview.Thumb == 1) review.ThumbsUp++;
|
||||
switch (r.Thumb)
|
||||
{
|
||||
case -1:
|
||||
boo++;
|
||||
break;
|
||||
case 1:
|
||||
yay++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
review.ThumbsDown = boo;
|
||||
review.ThumbsUp = yay;
|
||||
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
return this.Ok();
|
||||
|
@ -296,7 +326,7 @@ public class ReviewController : ControllerBase
|
|||
|
||||
XmlSerializer serializer = new(typeof(Review));
|
||||
Review? review = (Review?)serializer.Deserialize(new StringReader(bodyString));
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(review);
|
||||
return review;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
|
@ -43,6 +44,8 @@ public class ScoreController : ControllerBase
|
|||
Score? score = (Score?)serializer.Deserialize(new StringReader(bodyString));
|
||||
if (score == null) return this.BadRequest();
|
||||
|
||||
SanitizationHelper.SanitizeStringsInClass(score);
|
||||
|
||||
score.SlotId = id;
|
||||
|
||||
Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId);
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Linq;
|
|||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||
|
@ -93,7 +94,19 @@ public class UserController : ControllerBase
|
|||
|
||||
if (update == null) return this.BadRequest();
|
||||
|
||||
if (update.Biography != null) user.Biography = update.Biography;
|
||||
SanitizationHelper.SanitizeStringsInClass(update);
|
||||
|
||||
if (update.Biography != null)
|
||||
{
|
||||
if (update.Biography.Length > 100) return this.BadRequest();
|
||||
|
||||
user.Biography = update.Biography;
|
||||
}
|
||||
|
||||
foreach (string? resource in new[] {update.IconHash, update.YayHash, update.MehHash, update.BooHash, update.PlanetHash,})
|
||||
{
|
||||
if (resource != null && !FileHelper.ResourceExists(resource)) return this.BadRequest();
|
||||
}
|
||||
|
||||
if (update.IconHash != null) user.IconHash = update.IconHash;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using Kettu;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
|
@ -48,6 +49,8 @@ public class SlotPageController : ControllerBase
|
|||
return this.Redirect("~/slot/" + id);
|
||||
}
|
||||
|
||||
msg = SanitizationHelper.SanitizeString(msg);
|
||||
|
||||
await this.database.PostComment(user, id, CommentType.Level, msg);
|
||||
Logger.Log($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LoggerLevelComments.Instance);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
|||
using Kettu;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
|
@ -42,6 +43,8 @@ public class UserPageController : ControllerBase
|
|||
return this.Redirect("~/user/" + id);
|
||||
}
|
||||
|
||||
msg = SanitizationHelper.SanitizeString(msg);
|
||||
|
||||
await this.database.PostComment(user, id, CommentType.Profile, msg);
|
||||
Logger.Log($"Posted comment from {user.UserId}: \"{msg}\" on user {id}", LoggerLevelComments.Instance);
|
||||
|
||||
|
|
|
@ -138,6 +138,8 @@ public class Database : DbContext
|
|||
reaction = newReaction;
|
||||
}
|
||||
|
||||
rating = Math.Clamp(rating, -1, 1);
|
||||
|
||||
int oldRating = reaction.Rating;
|
||||
if (oldRating == rating) return true;
|
||||
|
||||
|
|
43
ProjectLighthouse/Helpers/SanitizationHelper.cs
Normal file
43
ProjectLighthouse/Helpers/SanitizationHelper.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
||||
public static class SanitizationHelper
|
||||
{
|
||||
|
||||
private static readonly Dictionary<string, string> charsToReplace = new() {
|
||||
{"<", "<"},
|
||||
{">", ">"},
|
||||
};
|
||||
|
||||
public static void SanitizeStringsInClass(object? instance)
|
||||
{
|
||||
if (instance == null) return;
|
||||
PropertyInfo[] properties = instance.GetType().GetProperties();
|
||||
foreach (PropertyInfo property in properties)
|
||||
{
|
||||
if (property.PropertyType != typeof(string)) continue;
|
||||
|
||||
string? before = (string?) property.GetValue(instance);
|
||||
|
||||
if (before == null) continue;
|
||||
if (!charsToReplace.Keys.Any(k => before.Contains(k))) continue;
|
||||
|
||||
property.SetValue(instance, SanitizeString(before));
|
||||
}
|
||||
}
|
||||
|
||||
public static string SanitizeString(string input)
|
||||
{
|
||||
|
||||
foreach ((string? key, string? value) in charsToReplace)
|
||||
{
|
||||
input = input.Replace(key, value);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Types.Reports;
|
||||
using LBPUnion.ProjectLighthouse.Types.Reviews;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs;
|
||||
|
||||
public class CleanupXmlInjection : IMaintenanceJob
|
||||
{
|
||||
private readonly Database database = new();
|
||||
public string Name() => "Sanitize user content";
|
||||
public string Description() => "Sanitizes all user-generated strings in levels, reviews, comments, users, and scores to prevent XML injection. Only needs to be run once.";
|
||||
|
||||
public async Task Run()
|
||||
{
|
||||
foreach (Slot slot in this.database.Slots) SanitizationHelper.SanitizeStringsInClass(slot);
|
||||
|
||||
foreach (Review review in this.database.Reviews) SanitizationHelper.SanitizeStringsInClass(review);
|
||||
|
||||
foreach (Comment comment in this.database.Comments) SanitizationHelper.SanitizeStringsInClass(comment);
|
||||
|
||||
foreach (Score score in this.database.Scores) SanitizationHelper.SanitizeStringsInClass(score);
|
||||
|
||||
foreach (User user in this.database.Users) SanitizationHelper.SanitizeStringsInClass(user);
|
||||
|
||||
foreach (Photo photo in this.database.Photos) SanitizationHelper.SanitizeStringsInClass(photo);
|
||||
|
||||
foreach (GriefReport report in this.database.Reports) SanitizationHelper.SanitizeStringsInClass(report);
|
||||
|
||||
await this.database.SaveChangesAsync();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
@page "/slot/{id:int}"
|
||||
@using System.Web
|
||||
@using LBPUnion.ProjectLighthouse.Helpers.Extensions
|
||||
@model LBPUnion.ProjectLighthouse.Pages.SlotPage
|
||||
|
||||
|
@ -31,7 +32,7 @@
|
|||
<div class="eight wide column">
|
||||
<div class="ui blue segment">
|
||||
<h2>Description</h2>
|
||||
<p>@(string.IsNullOrEmpty(Model.Slot.Description) ? "This level has no description." : Model.Slot.Description)</p>
|
||||
<p>@HttpUtility.HtmlDecode(string.IsNullOrEmpty(Model.Slot.Description) ? "This level has no description." : Model.Slot.Description)</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="eight wide column">
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@page "/user/{userId:int}"
|
||||
@using System.Web
|
||||
@using LBPUnion.ProjectLighthouse.Helpers.Extensions
|
||||
@using LBPUnion.ProjectLighthouse.Types
|
||||
@model LBPUnion.ProjectLighthouse.Pages.UserPage
|
||||
|
@ -82,7 +83,7 @@
|
|||
}
|
||||
else
|
||||
{
|
||||
<p>@Model.ProfileUser.Biography</p>
|
||||
<p>@HttpUtility.HtmlDecode(Model.ProfileUser.Biography)</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -120,14 +121,14 @@
|
|||
|
||||
@if (!Model.ProfileUser.Banned)
|
||||
{
|
||||
<a class="ui red button" href="/admin/user/@Model.ProfileUser.UserId/ban">
|
||||
<i class="ban icon"></i>
|
||||
<span>Ban User</span>
|
||||
</a>
|
||||
|
||||
<br><br>
|
||||
<div>
|
||||
<a class="ui red button" href="/admin/user/@Model.ProfileUser.UserId/ban">
|
||||
<i class="ban icon"></i>
|
||||
<span>Ban User</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="ui fitted hidden divider"></div>
|
||||
}
|
||||
|
||||
@await Html.PartialAsync("Partials/AdminSetGrantedSlotsFormPartial", Model.ProfileUser)
|
||||
</div>
|
||||
}
|
||||
}
|
|
@ -131,12 +131,12 @@ public class Slot
|
|||
[XmlIgnore]
|
||||
[NotMapped]
|
||||
[JsonIgnore]
|
||||
public int Hearts => database.HeartedLevels.Count(s => s.SlotId == this.SlotId);
|
||||
public int Hearts => this.database.HeartedLevels.Count(s => s.SlotId == this.SlotId);
|
||||
|
||||
[XmlIgnore]
|
||||
[NotMapped]
|
||||
[JsonIgnore]
|
||||
public int Comments => database.Comments.Count(c => c.Type == CommentType.Level && c.TargetId == this.SlotId);
|
||||
public int Comments => this.database.Comments.Count(c => c.Type == CommentType.Level && c.TargetId == this.SlotId);
|
||||
|
||||
[XmlIgnore]
|
||||
[NotMapped]
|
||||
|
@ -201,19 +201,19 @@ public class Slot
|
|||
[NotMapped]
|
||||
[JsonIgnore]
|
||||
[XmlElement("thumbsup")]
|
||||
public int Thumbsup => database.RatedLevels.Count(r => r.SlotId == this.SlotId && r.Rating == 1);
|
||||
public int Thumbsup => this.database.RatedLevels.Count(r => r.SlotId == this.SlotId && r.Rating == 1);
|
||||
|
||||
[NotMapped]
|
||||
[JsonIgnore]
|
||||
[XmlElement("thumbsdown")]
|
||||
public int Thumbsdown => database.RatedLevels.Count(r => r.SlotId == this.SlotId && r.Rating == -1);
|
||||
public int Thumbsdown => this.database.RatedLevels.Count(r => r.SlotId == this.SlotId && r.Rating == -1);
|
||||
|
||||
[NotMapped]
|
||||
[JsonPropertyName("averageRating")]
|
||||
[XmlElement("averageRating")]
|
||||
public double RatingLBP1 {
|
||||
get {
|
||||
IQueryable<RatedLevel> ratedLevels = database.RatedLevels.Where(r => r.SlotId == this.SlotId && r.RatingLBP1 > 0);
|
||||
IQueryable<RatedLevel> ratedLevels = this.database.RatedLevels.Where(r => r.SlotId == this.SlotId && r.RatingLBP1 > 0);
|
||||
if (!ratedLevels.Any()) return 3.0;
|
||||
|
||||
return Enumerable.Average(ratedLevels, r => r.RatingLBP1);
|
||||
|
@ -223,7 +223,7 @@ public class Slot
|
|||
[NotMapped]
|
||||
[JsonIgnore]
|
||||
[XmlElement("reviewCount")]
|
||||
public int ReviewCount => database.Reviews.Count(r => r.SlotId == this.SlotId);
|
||||
public int ReviewCount => this.database.Reviews.Count(r => r.SlotId == this.SlotId);
|
||||
|
||||
[XmlElement("leveltype")]
|
||||
public string LevelType { get; set; } = "";
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#nullable enable
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
|
@ -21,19 +19,19 @@ public class Review
|
|||
public int ReviewerId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(ReviewerId))]
|
||||
public User Reviewer { get; set; }
|
||||
public User? Reviewer { get; set; }
|
||||
|
||||
[XmlElement("slot_id")]
|
||||
public int SlotId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(SlotId))]
|
||||
public Slot Slot { get; set; }
|
||||
public Slot? Slot { get; set; }
|
||||
|
||||
[XmlElement("timestamp")]
|
||||
public long Timestamp { get; set; }
|
||||
|
||||
[XmlElement("labels")]
|
||||
public string LabelCollection { get; set; }
|
||||
public string LabelCollection { get; set; } = "";
|
||||
|
||||
[NotMapped]
|
||||
[XmlIgnore]
|
||||
|
@ -49,7 +47,7 @@ public class Review
|
|||
public DeletedBy DeletedBy { get; set; }
|
||||
|
||||
[XmlElement("text")]
|
||||
public string Text { get; set; }
|
||||
public string Text { get; set; } = "";
|
||||
|
||||
[XmlElement("thumb")]
|
||||
public int Thumb { get; set; }
|
||||
|
@ -66,27 +64,26 @@ public class Review
|
|||
|
||||
public string Serialize(string elementOverride, RatedLevel? yourLevelRating = null, RatedReview? yourRatingStats = null)
|
||||
{
|
||||
string deletedBy = this.DeletedBy switch
|
||||
{
|
||||
DeletedBy.None => "none",
|
||||
DeletedBy.Moderator => "moderator",
|
||||
DeletedBy.LevelAuthor => "level_author",
|
||||
_ => "none",
|
||||
};
|
||||
|
||||
XmlWriterSettings settings = new();
|
||||
settings.OmitXmlDeclaration = true;
|
||||
|
||||
XmlSerializer serializer = new(typeof(DeletedBy));
|
||||
StringWriter stringWriter = new();
|
||||
using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, settings)) serializer.Serialize(xmlWriter, this.DeletedBy);
|
||||
string deletedBy = stringWriter.ToString();
|
||||
|
||||
string reviewData = LbpSerializer.TaggedStringElement("slot_id", this.SlotId, "type", this.Slot.Type) +
|
||||
LbpSerializer.StringElement("reviewer", this.Reviewer.Username) +
|
||||
string reviewData = LbpSerializer.TaggedStringElement("slot_id", this.SlotId, "type", this.Slot?.Type) +
|
||||
LbpSerializer.StringElement("reviewer", this.Reviewer?.Username) +
|
||||
LbpSerializer.StringElement("thumb", this.Thumb) +
|
||||
LbpSerializer.StringElement("timestamp", this.Timestamp) +
|
||||
LbpSerializer.StringElement("labels", this.LabelCollection) +
|
||||
LbpSerializer.StringElement("deleted", this.Deleted) +
|
||||
deletedBy +
|
||||
LbpSerializer.StringElement("deleted_by", deletedBy) +
|
||||
LbpSerializer.StringElement("text", this.Text) +
|
||||
LbpSerializer.StringElement("thumbsup", this.ThumbsUp) +
|
||||
LbpSerializer.StringElement("thumbsdown", this.ThumbsDown) +
|
||||
LbpSerializer.StringElement("yourthumb", yourRatingStats?.Thumb == null ? 0 : yourRatingStats?.Thumb);
|
||||
LbpSerializer.StringElement("yourthumb", yourRatingStats?.Thumb ?? 0);
|
||||
|
||||
return LbpSerializer.TaggedStringElement(elementOverride, reviewData, "id", this.SlotId + "." + this.Reviewer.Username);
|
||||
return LbpSerializer.TaggedStringElement(elementOverride, reviewData, "id", this.SlotId + "." + this.Reviewer?.Username);
|
||||
}
|
||||
}
|
|
@ -7,23 +7,23 @@ namespace LBPUnion.ProjectLighthouse.Types;
|
|||
public class UserUpdate
|
||||
{
|
||||
[XmlElement("location")]
|
||||
public Location? Location;
|
||||
public Location? Location { get; set; }
|
||||
|
||||
[XmlElement("biography")]
|
||||
public string? Biography;
|
||||
public string? Biography { get; set; }
|
||||
|
||||
[XmlElement("icon")]
|
||||
public string? IconHash;
|
||||
public string? IconHash { get; set; }
|
||||
|
||||
[XmlElement("planets")]
|
||||
public string? PlanetHash;
|
||||
public string? PlanetHash { get; set; }
|
||||
|
||||
[XmlElement("yay2")]
|
||||
public string? YayHash;
|
||||
public string? YayHash { get; set; }
|
||||
|
||||
[XmlElement("meh2")]
|
||||
public string? MehHash;
|
||||
public string? MehHash { get; set; }
|
||||
|
||||
[XmlElement("boo2")]
|
||||
public string? BooHash;
|
||||
public string? BooHash { get; set; }
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue