mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-05-16 14:42:28 +00:00
Improvements to website comments and good grief reports along with numerous bug fixes
This commit is contained in:
parent
e4b4984505
commit
aacc2d5eaf
30 changed files with 882 additions and 148 deletions
|
@ -33,49 +33,9 @@ public class CommentController : ControllerBase
|
|||
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
Comment? comment = await this.database.Comments.Include(c => c.Poster).FirstOrDefaultAsync(c => commentId == c.CommentId);
|
||||
bool success = await this.database.RateComment(user, commentId, rating);
|
||||
if (!success) return this.BadRequest();
|
||||
|
||||
if (comment == null) return this.BadRequest();
|
||||
|
||||
Reaction? reaction = await this.database.Reactions.FirstOrDefaultAsync(r => r.UserId == user.UserId && r.TargetId == commentId);
|
||||
if (reaction == null)
|
||||
{
|
||||
Reaction newReaction = new Reaction()
|
||||
{
|
||||
UserId = user.UserId,
|
||||
TargetId = commentId,
|
||||
Rating = 0,
|
||||
};
|
||||
this.database.Reactions.Add(newReaction);
|
||||
await this.database.SaveChangesAsync();
|
||||
reaction = newReaction;
|
||||
}
|
||||
int oldRating = reaction.Rating;
|
||||
if (oldRating == rating) return this.Ok();
|
||||
|
||||
reaction.Rating = rating;
|
||||
// if rating changed then we count the number of reactions to ensure accuracy
|
||||
List<Reaction> reactions = await this.database.Reactions
|
||||
.Where(c => c.TargetId == commentId)
|
||||
.ToListAsync();
|
||||
int yay = 0;
|
||||
int boo = 0;
|
||||
foreach (Reaction r in reactions)
|
||||
{
|
||||
switch (r.Rating)
|
||||
{
|
||||
case -1:
|
||||
boo++;
|
||||
break;
|
||||
case 1:
|
||||
yay++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
comment.ThumbsDown = boo;
|
||||
comment.ThumbsUp = yay;
|
||||
await this.database.SaveChangesAsync();
|
||||
return this.Ok();
|
||||
}
|
||||
|
||||
|
@ -135,27 +95,12 @@ public class CommentController : ControllerBase
|
|||
|
||||
int targetId = slotId.GetValueOrDefault();
|
||||
|
||||
if (type == CommentType.Profile)
|
||||
{
|
||||
User? target = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||
if (target == null) return this.BadRequest();
|
||||
targetId = target.UserId;
|
||||
}
|
||||
else
|
||||
{
|
||||
Slot? target = await this.database.Slots.FirstOrDefaultAsync(u => u.SlotId == slotId);
|
||||
if (target == null) return this.BadRequest();
|
||||
}
|
||||
if (type == CommentType.Profile) targetId = this.database.Users.First(u => u.Username == username).UserId;
|
||||
|
||||
comment.PosterUserId = poster.UserId;
|
||||
comment.TargetId = targetId;
|
||||
comment.Type = type;
|
||||
bool success = await this.database.PostComment(poster, targetId, type, comment.Message);
|
||||
if (success) return this.Ok();
|
||||
|
||||
comment.Timestamp = TimeHelper.UnixTimeMilliseconds();
|
||||
|
||||
this.database.Comments.Add(comment);
|
||||
await this.database.SaveChangesAsync();
|
||||
return this.Ok();
|
||||
return this.BadRequest();
|
||||
}
|
||||
|
||||
[HttpPost("deleteUserComment/{username}")]
|
||||
|
|
56
ProjectLighthouse/Controllers/GameApi/ReportController.cs
Normal file
56
ProjectLighthouse/Controllers/GameApi/ReportController.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using Kettu;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using LBPUnion.ProjectLighthouse.Types.Reports;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Controllers.GameApi;
|
||||
|
||||
[ApiController]
|
||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||
[Produces("text/xml")]
|
||||
public class ReportController : ControllerBase
|
||||
{
|
||||
private readonly Database database;
|
||||
|
||||
public ReportController(Database database)
|
||||
{
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
[HttpPost("grief")]
|
||||
public async Task<IActionResult> Report()
|
||||
{
|
||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||
if (user == null) return this.StatusCode(403, "");
|
||||
|
||||
this.Request.Body.Position = 0;
|
||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||
|
||||
Console.WriteLine(bodyString);
|
||||
XmlSerializer serializer = new(typeof(GriefReport));
|
||||
GriefReport? report = (GriefReport?) serializer.Deserialize(new StringReader(bodyString));
|
||||
|
||||
if (report == null) return this.BadRequest();
|
||||
|
||||
report.Bounds = JsonSerializer.Serialize(report.XmlBounds.Rect, typeof(Rectangle));
|
||||
report.Players = JsonSerializer.Serialize(report.XmlPlayers, typeof(ReportPlayer[]));
|
||||
report.VisiblePlayers = JsonSerializer.Serialize(report.XmlVisiblePlayers, typeof(VisiblePlayer[]));
|
||||
report.Timestamp = TimeHelper.UnixTimeMilliseconds();
|
||||
report.ReportingPlayerId = user.UserId;
|
||||
|
||||
this.database.Reports.Add(report);
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
return this.Ok();
|
||||
}
|
||||
|
||||
}
|
|
@ -55,7 +55,7 @@ public class ResourcesController : ControllerBase
|
|||
[HttpGet("/gameAssets/{hash}")]
|
||||
public IActionResult GetGameImage(string hash)
|
||||
{
|
||||
string path = $"png/{hash}.png";
|
||||
string path = $"png{Path.DirectorySeparatorChar}{hash}.png";
|
||||
|
||||
if (IOFile.Exists(path))
|
||||
{
|
||||
|
|
|
@ -23,6 +23,32 @@ public class SlotPageController : ControllerBase
|
|||
this.database = database;
|
||||
}
|
||||
|
||||
[HttpGet("rateComment")]
|
||||
public async Task<IActionResult> RateComment([FromRoute] int id, [FromQuery] int commentId, [FromQuery] int rating)
|
||||
{
|
||||
User? user = this.database.UserFromWebRequest(this.Request);
|
||||
if (user == null) return this.Redirect("~/login");
|
||||
|
||||
await this.database.RateComment(user,
|
||||
commentId,
|
||||
rating);
|
||||
|
||||
return this.Redirect("~/slot/" + id + "#" + commentId);
|
||||
}
|
||||
|
||||
[HttpGet("postComment")]
|
||||
public async Task<IActionResult> PostComment([FromRoute] int id, [FromQuery] string msg)
|
||||
{
|
||||
User? user = this.database.UserFromWebRequest(this.Request);
|
||||
if (user == null) return this.Redirect("~/login");
|
||||
|
||||
bool success = await this.database.PostComment(user, id, CommentType.Level, msg);
|
||||
|
||||
if (!success) return this.NotFound();
|
||||
|
||||
return this.Redirect("~/slot/" + id);
|
||||
}
|
||||
|
||||
[HttpGet("heart")]
|
||||
public async Task<IActionResult> HeartLevel([FromRoute] int id, [FromQuery] string? callbackUrl)
|
||||
{
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Controllers.Website;
|
||||
|
@ -31,6 +34,30 @@ public class UserPageController : ControllerBase
|
|||
return this.Redirect("~/user/" + id);
|
||||
}
|
||||
|
||||
[HttpGet("rateComment")]
|
||||
public async Task<IActionResult> RateComment([FromRoute] int id, [FromQuery] int commentId, [FromQuery] int rating)
|
||||
{
|
||||
User? user = this.database.UserFromWebRequest(this.Request);
|
||||
if (user == null) return this.Redirect("~/login");
|
||||
|
||||
await this.database.RateComment(user, commentId, rating);
|
||||
|
||||
return this.Redirect("~/user/" + id + "#" + commentId);
|
||||
}
|
||||
|
||||
[HttpGet("postComment")]
|
||||
public async Task<IActionResult> PostComment([FromRoute] int id, [FromQuery] string msg)
|
||||
{
|
||||
User? user = this.database.UserFromWebRequest(this.Request);
|
||||
if (user == null) return this.Redirect("~/login");
|
||||
|
||||
bool success = await this.database.PostComment(user, id, CommentType.Profile, msg);
|
||||
|
||||
if (!success) return this.NotFound();
|
||||
|
||||
return this.Redirect("~/user/" + id);
|
||||
}
|
||||
|
||||
[HttpGet("unheart")]
|
||||
public async Task<IActionResult> UnheartUser([FromRoute] int id)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
@ -6,6 +7,7 @@ using LBPUnion.ProjectLighthouse.Types;
|
|||
using LBPUnion.ProjectLighthouse.Types.Categories;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Types.Reports;
|
||||
using LBPUnion.ProjectLighthouse.Types.Reviews;
|
||||
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||
using LBPUnion.ProjectLighthouse.Types.Tickets;
|
||||
|
@ -37,6 +39,7 @@ public class Database : DbContext
|
|||
public DbSet<UserApprovedIpAddress> UserApprovedIpAddresses { get; set; }
|
||||
public DbSet<DatabaseCategory> CustomCategories { get; set; }
|
||||
public DbSet<Reaction> Reactions { get; set; }
|
||||
public DbSet<GriefReport> Reports { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
=> options.UseMySql(ServerSettings.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
|
||||
|
@ -89,6 +92,83 @@ public class Database : DbContext
|
|||
|
||||
#region Hearts & Queues
|
||||
|
||||
public async Task<bool> RateComment(User user, int commentId, int rating)
|
||||
{
|
||||
Comment? comment = await this.Comments.FirstOrDefaultAsync(c => commentId == c.CommentId);
|
||||
|
||||
if (comment == null) return false;
|
||||
|
||||
if (comment.PosterUserId == user.UserId) return false;
|
||||
|
||||
Reaction? reaction = await this.Reactions.FirstOrDefaultAsync(r => r.UserId == user.UserId && r.TargetId == commentId);
|
||||
if (reaction == null)
|
||||
{
|
||||
Reaction newReaction = new()
|
||||
{
|
||||
UserId = user.UserId,
|
||||
TargetId = commentId,
|
||||
Rating = 0,
|
||||
};
|
||||
this.Reactions.Add(newReaction);
|
||||
await this.SaveChangesAsync();
|
||||
reaction = newReaction;
|
||||
}
|
||||
|
||||
int oldRating = reaction.Rating;
|
||||
if (oldRating == rating) return true;
|
||||
|
||||
reaction.Rating = rating;
|
||||
// if rating changed then we count the number of reactions to ensure accuracy
|
||||
List<Reaction> reactions = await this.Reactions.Where(c => c.TargetId == commentId).ToListAsync();
|
||||
int yay = 0;
|
||||
int boo = 0;
|
||||
foreach (Reaction r in reactions)
|
||||
{
|
||||
switch (r.Rating)
|
||||
{
|
||||
case -1:
|
||||
boo++;
|
||||
break;
|
||||
case 1:
|
||||
yay++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
comment.ThumbsDown = boo;
|
||||
comment.ThumbsUp = yay;
|
||||
await this.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> PostComment(User user, int targetId, CommentType type, string message)
|
||||
{
|
||||
if (type == CommentType.Profile)
|
||||
{
|
||||
User? targetUser = await this.Users.FirstOrDefaultAsync(u => u.UserId == targetId);
|
||||
if (targetUser == null) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Slot? targetSlot = await this.Slots.FirstOrDefaultAsync(u => u.SlotId == targetId);
|
||||
if(targetSlot == null) return false;
|
||||
}
|
||||
|
||||
this.Comments.Add
|
||||
(
|
||||
new Comment
|
||||
{
|
||||
PosterUserId = user.UserId,
|
||||
TargetId = targetId,
|
||||
Type = type,
|
||||
Message = message,
|
||||
Timestamp = TimeHelper.UnixTimeMilliseconds(),
|
||||
}
|
||||
);
|
||||
await this.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task HeartUser(User user, User heartedUser)
|
||||
{
|
||||
HeartedProfile? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId);
|
||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Runtime.Intrinsics.Arm;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -11,7 +12,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers;
|
|||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static class HashHelper
|
||||
{
|
||||
private static readonly SHA1 sha1 = SHA1.Create();
|
||||
// private static readonly SHA1 sha1 = SHA1.Create();
|
||||
private static readonly SHA256 sha256 = SHA256.Create();
|
||||
private static readonly Random random = new();
|
||||
|
||||
|
@ -71,7 +72,7 @@ public static class HashHelper
|
|||
|
||||
public static string Sha1Hash(string str) => Sha1Hash(Encoding.UTF8.GetBytes(str));
|
||||
|
||||
public static string Sha1Hash(byte[] bytes) => BitConverter.ToString(sha1.ComputeHash(bytes)).Replace("-", "");
|
||||
public static string Sha1Hash(byte[] bytes) => BitConverter.ToString(SHA1.Create().ComputeHash(bytes)).Replace("-","");
|
||||
|
||||
public static string BCryptHash(string str) => BCrypt.Net.BCrypt.HashPassword(str);
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ public static class ImageHelper
|
|||
{
|
||||
if (type != LbpFileType.Jpeg && type != LbpFileType.Png && type != LbpFileType.Texture) return false;
|
||||
|
||||
if (File.Exists($"png/{hash}.png")) return true;
|
||||
if (File.Exists($"png{Path.DirectorySeparatorChar}{hash}.png")) return true;
|
||||
|
||||
using MemoryStream ms = new(data);
|
||||
using BinaryReader reader = new(ms);
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
using LBPUnion.ProjectLighthouse;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ProjectLighthouse.Migrations
|
||||
{
|
||||
[DbContext(typeof(Database))]
|
||||
[Migration("20220212041106_AddGriefReports")]
|
||||
public partial class AddGriefReports : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Reports",
|
||||
columns: table => new
|
||||
{
|
||||
ReportId = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Type = table.Column<int>(type: "int", nullable: false),
|
||||
Timestamp = table.Column<long>(type: "bigint", nullable: false),
|
||||
VisiblePlayers = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
ReportingPlayerId = table.Column<int>(type: "int", nullable: false),
|
||||
Players = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
GriefStateHash = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
LevelOwner = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
InitialStateHash = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
JpegHash = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
LevelId = table.Column<int>(type: "int", nullable: false),
|
||||
LevelType = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Bounds = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Reports", x => x.ReportId);
|
||||
table.ForeignKey(
|
||||
name: "FK_Reports_Users_ReportingPlayerId",
|
||||
column: x => x.ReportingPlayerId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "UserId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Reports_ReportingPlayerId",
|
||||
table: "Reports",
|
||||
column: "ReportingPlayerId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Reports");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ namespace ProjectLighthouse.Migrations
|
|||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.0")
|
||||
.HasAnnotation("ProductVersion", "6.0.2")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b =>
|
||||
|
@ -503,6 +503,55 @@ namespace ProjectLighthouse.Migrations
|
|||
b.ToTable("Reactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reports.GriefReport", b =>
|
||||
{
|
||||
b.Property<int>("ReportId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Bounds")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("GriefStateHash")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("InitialStateHash")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("JpegHash")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("LevelId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("LevelOwner")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("LevelType")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Players")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("ReportingPlayerId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long>("Timestamp")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("VisiblePlayers")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("ReportId");
|
||||
|
||||
b.HasIndex("ReportingPlayerId");
|
||||
|
||||
b.ToTable("Reports");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b =>
|
||||
{
|
||||
b.Property<int>("RatedReviewId")
|
||||
|
@ -862,6 +911,17 @@ namespace ProjectLighthouse.Migrations
|
|||
b.Navigation("Poster");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reports.GriefReport", b =>
|
||||
{
|
||||
b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "ReportingPlayer")
|
||||
.WithMany()
|
||||
.HasForeignKey("ReportingPlayerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ReportingPlayer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b =>
|
||||
{
|
||||
b.HasOne("LBPUnion.ProjectLighthouse.Types.Reviews.Review", "Review")
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
@if (Model.User.IsAdmin)
|
||||
{
|
||||
Model.NavigationItems.Add(new PageNavigationItem("Reports", "/reports/0", "exclamation circle"));
|
||||
Model.NavigationItemsRight.Add(new PageNavigationItem("Admin Panel", "/admin", "cogs"));
|
||||
}
|
||||
Model.NavigationItemsRight.Add(new PageNavigationItem("Log out", "/logout", "user alternate slash")); // should always be last
|
||||
|
|
74
ProjectLighthouse/Pages/Partials/CommentsPartial.cshtml
Normal file
74
ProjectLighthouse/Pages/Partials/CommentsPartial.cshtml
Normal file
|
@ -0,0 +1,74 @@
|
|||
@using System.IO
|
||||
@using System.Web
|
||||
@using LBPUnion.ProjectLighthouse.Types.Profiles
|
||||
|
||||
<div class="ui yellow segment" id="comments">
|
||||
<style>
|
||||
.comment {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
}
|
||||
.voting {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
</style>
|
||||
<h1>Comments</h1>
|
||||
@if (Model.Comments.Count == 0)
|
||||
{
|
||||
<p>There are no comments.</p>
|
||||
}
|
||||
|
||||
@for (int i = 0; i < Model.Comments.Count; i++)
|
||||
{
|
||||
Comment comment = Model.Comments[i];
|
||||
DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(comment.Timestamp / 1000);
|
||||
StringWriter messageWriter = new();
|
||||
HttpUtility.HtmlDecode(comment.getComment(), messageWriter);
|
||||
string decodedMessage = messageWriter.ToString();
|
||||
string url = Url.RouteUrl(ViewContext.RouteData.Values);
|
||||
int rating = comment.ThumbsUp - comment.ThumbsDown;
|
||||
<div style="display: flex" id="@comment.CommentId">
|
||||
<div class="voting">
|
||||
<a href="@url/rateComment?commentId=@(comment.CommentId)&rating=@(comment.YourThumb == 1 ? 0 : 1)">
|
||||
<i class="fitted @(comment.YourThumb == 1 ? "green" : "grey") arrow up link icon" style="display: block"></i>
|
||||
</a>
|
||||
<span style="text-align: center; margin: auto; @(rating < 0 ? "margin-left: -5px" : "")">@(rating)</span>
|
||||
<a href="@url/rateComment?commentId=@(comment.CommentId)&rating=@(comment.YourThumb == -1 ? 0 : -1)">
|
||||
<i class="fitted @(comment.YourThumb == -1 ? "red" : "grey") arrow down link icon" style="display: block"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="comment">
|
||||
<b><a href="/user/@comment.PosterUserId">@comment.Poster.Username</a>: </b>
|
||||
@if (comment.Deleted)
|
||||
{
|
||||
<i><span>@decodedMessage</span></i>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@decodedMessage</span>
|
||||
}
|
||||
<p>
|
||||
<i>@timestamp.ToString("MM/dd/yyyy @ h:mm tt") UTC</i>
|
||||
</p>
|
||||
@if (i != Model.Comments.Count - 1)
|
||||
{
|
||||
<div class="ui divider"></div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if(Model.CommentsEnabled){
|
||||
<div class="ui divider"></div>
|
||||
<form class="ui reply form" action="@Url.RouteUrl(ViewContext.RouteData.Values)/postComment">
|
||||
<div class="field">
|
||||
<textarea style="min-height: 70px; height: 70px; max-height:120px" name="msg"></textarea>
|
||||
</div>
|
||||
<input type="submit" class="ui blue button">
|
||||
</form>
|
||||
}
|
||||
</div>
|
|
@ -31,16 +31,17 @@ public class PhotosPage : BaseLayout
|
|||
{
|
||||
if (string.IsNullOrWhiteSpace(name)) name = "";
|
||||
|
||||
this.PhotoCount = await this.Database.Photos.CountAsync(p => p.Creator.Username.Contains(name) || p.PhotoSubjectCollection.Contains(name));
|
||||
this.SearchValue = name.Replace(" ", string.Empty);
|
||||
|
||||
this.PhotoCount = await this.Database.Photos.CountAsync(p => p.Creator.Username.Contains(this.SearchValue) || p.PhotoSubjectCollection.Contains(this.SearchValue));
|
||||
|
||||
this.SearchValue = name;
|
||||
this.PageNumber = pageNumber;
|
||||
this.PageAmount = Math.Max(1, (int)Math.Ceiling((double)this.PhotoCount / ServerStatics.PageSize));
|
||||
|
||||
if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) return this.Redirect($"/photos/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}");
|
||||
|
||||
this.Photos = await this.Database.Photos.Include(p => p.Creator)
|
||||
.Where(p => p.Creator.Username.Contains(name) || p.PhotoSubjectCollection.Contains(name))
|
||||
.Where(p => p.Creator.Username.Contains(this.SearchValue) || p.PhotoSubjectCollection.Contains(this.SearchValue))
|
||||
.OrderByDescending(p => p.Timestamp)
|
||||
.Skip(pageNumber * ServerStatics.PageSize)
|
||||
.Take(ServerStatics.PageSize)
|
||||
|
|
206
ProjectLighthouse/Pages/ReportsPage.cshtml
Normal file
206
ProjectLighthouse/Pages/ReportsPage.cshtml
Normal file
|
@ -0,0 +1,206 @@
|
|||
@page "/reports/{pageNumber:int}"
|
||||
@using LBPUnion.ProjectLighthouse.Types.Reports
|
||||
@model LBPUnion.ProjectLighthouse.Pages.ReportsPage
|
||||
|
||||
@{
|
||||
Layout = "Layouts/BaseLayout";
|
||||
Model.Title = "Reports";
|
||||
}
|
||||
|
||||
<p>There are @Model.ReportCount total reports!</p>
|
||||
|
||||
<form action="/reports/0">
|
||||
<div class="ui icon input">
|
||||
<input type="text" name="name" placeholder="Search reports..." value="@Model.SearchValue">
|
||||
<i class="search icon"></i>
|
||||
</div>
|
||||
</form>
|
||||
<div class="ui divider"></div>
|
||||
<script>
|
||||
let subjects = [];
|
||||
let bounds = [];
|
||||
let canvases = [];
|
||||
let ctx = [];
|
||||
let images = [];
|
||||
</script>
|
||||
@foreach (GriefReport report in Model.Reports)
|
||||
{
|
||||
<div class="ui segment">
|
||||
<div>
|
||||
<canvas class="hide-subjects" id="canvas-subjects-@report.ReportId" width="1920" height="1080"
|
||||
style="position: absolute; transform: rotate(180deg)">
|
||||
</canvas>
|
||||
<img id="game-image-@report.ReportId" src="/gameAssets/@report.JpegHash" alt="Grief report picture" style="width: 100%; height: auto; border-radius: .28571429rem;">
|
||||
</div>
|
||||
<p><i>Report submitted by <b><a href="/user/@report.ReportingPlayerId">@report.ReportingPlayer.Username</a></b></i></p>
|
||||
<b class="hover-players" id="hover-subjects-2-@report.ReportId">Report contains @report.XmlPlayers.Length @(report.XmlPlayers.Length == 1 ? "player" : "players")</b>
|
||||
@foreach (ReportPlayer player in report.XmlPlayers)
|
||||
{
|
||||
<div id="hover-subjects-@report.ReportId" class="hover-players">
|
||||
<a href="/">@player.Name</a>
|
||||
</div>
|
||||
}
|
||||
<div><b>Report time: </b>@(DateTimeOffset.FromUnixTimeMilliseconds(report.Timestamp).ToString("R"))</div>
|
||||
<div><b>Report reason: </b>@report.Type</div>
|
||||
<div><b>Level ID:</b> @report.LevelId</div>
|
||||
<div><b>Level type:</b> @report.LevelType</div>
|
||||
<div><b>Level owner:</b> @report.LevelOwner</div>
|
||||
<div id="hover-bounds-@report.ReportId" class="hover-region"><b>Hover to see reported region</b></div>
|
||||
</div>
|
||||
<script>
|
||||
subjects[@report.ReportId] = @Html.Raw(report.Players)
|
||||
bounds[@report.ReportId] = @Html.Raw(report.Bounds)
|
||||
images[@report.ReportId] = document.getElementById("game-image-@report.ReportId")
|
||||
canvases[@report.ReportId] = document.getElementById("canvas-subjects-@report.ReportId")
|
||||
canvases[@report.ReportId].width = images[@report.ReportId].offsetWidth;
|
||||
canvases[@report.ReportId].height = images[@report.ReportId].clientHeight;
|
||||
ctx[@report.ReportId] = canvases[@report.ReportId].getContext('2d');
|
||||
</script>
|
||||
}
|
||||
<script>
|
||||
function getReportId(name){
|
||||
let split = name.split("-");
|
||||
return split[split.length-1];
|
||||
}
|
||||
const colours = ["#96dd3c", "#ceb424", "#cc0a1d", "#c800cc"];
|
||||
let displayType;
|
||||
window.addEventListener("load", function () {
|
||||
document.querySelectorAll(".hover-players").forEach(item => {
|
||||
item.addEventListener('mouseenter', function () {
|
||||
let reportId = getReportId(item.id);
|
||||
displayType = 1;
|
||||
canvases[reportId].className = "photo-subjects";
|
||||
redraw(reportId);
|
||||
});
|
||||
});
|
||||
document.querySelectorAll(".hover-region").forEach(item => {
|
||||
item.addEventListener('mouseenter', function () {
|
||||
let reportId = getReportId(item.id);
|
||||
displayType = 0;
|
||||
canvases[reportId].className = "photo-subjects";
|
||||
redraw(reportId);
|
||||
});
|
||||
});
|
||||
document.querySelectorAll(".hover-region, .hover-players").forEach(item => {
|
||||
item.addEventListener('mouseleave', function () {
|
||||
canvases[getReportId(item.id)].className = "photo-subjects hide-subjects";
|
||||
});
|
||||
});
|
||||
}, false);
|
||||
function redraw(reportId){
|
||||
let context = ctx[reportId];
|
||||
let canvas = canvases[reportId];
|
||||
let image = images[reportId];
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
let w = canvas.width;
|
||||
let h = canvas.height;
|
||||
|
||||
// halfwidth, halfheight
|
||||
const hw = w / 2;
|
||||
const hh = h / 2;
|
||||
switch (displayType){
|
||||
case 0: {
|
||||
let imageBounds = bounds[reportId];
|
||||
const x1 = imageBounds.Left;
|
||||
const x2 = imageBounds.Right;
|
||||
const y1 = imageBounds.Top;
|
||||
const y2 = imageBounds.Bottom;
|
||||
const scaleX = image.naturalWidth / canvas.width;
|
||||
const scaleY = image.naturalHeight / canvas.height;
|
||||
const bx = canvas.width-(x2/scaleX);
|
||||
const by = canvas.height-(y2/scaleY);
|
||||
const bw = (x2 - x1) / scaleX;
|
||||
const bh = (y2 - y1) / scaleY;
|
||||
context.beginPath();
|
||||
context.lineWidth = 3;
|
||||
context.globalAlpha = 0.6;
|
||||
context.fillStyle = "black";
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
context.clearRect(bx, by, bw, bh);
|
||||
context.globalAlpha = 1.0;
|
||||
context.beginPath();
|
||||
context.lineWidth = 3;
|
||||
context.strokeStyle = "#cc0a1d";
|
||||
context.rect(bx, by, bw, bh);
|
||||
context.stroke();
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
let subject = subjects[reportId];
|
||||
subject.forEach((s, si) => {
|
||||
const colour = colours[si % 4];
|
||||
|
||||
// Bounding box
|
||||
const x1 = s.Location.Left;
|
||||
const x2 = s.Location.Right;
|
||||
const y1 = s.Location.Top;
|
||||
const y2 = s.Location.Bottom;
|
||||
|
||||
const scaleX = image.naturalWidth / canvas.width;
|
||||
const scaleY = image.naturalHeight / canvas.height;
|
||||
|
||||
|
||||
const bx = canvas.width-(x2/scaleX);
|
||||
const by = canvas.height-(y2/scaleY);
|
||||
const bw = (x2 - x1) / scaleX;
|
||||
const bh = (y2 - y1) / scaleY;
|
||||
|
||||
context.beginPath();
|
||||
context.lineWidth = 3;
|
||||
context.strokeStyle = colour;
|
||||
context.rect(bx, by, bw, bh);
|
||||
context.stroke();
|
||||
|
||||
// Move into relative coordinates from bounding box
|
||||
context.translate(bx, by);
|
||||
|
||||
// Username label
|
||||
context.font = "16px Lato";
|
||||
context.fillStyle = colour;
|
||||
|
||||
// Text width/height for the label background
|
||||
const tw = context.measureText(s.Name).width;
|
||||
const th = 24;
|
||||
|
||||
// Check if the label will flow off the bottom of the frame
|
||||
const overflowBottom = (y2+tw - 24) > (canvas.width);
|
||||
// Check if the label will flow off the left of the frame
|
||||
const overflowLeft = (x2) < (24);
|
||||
|
||||
// Set alignment
|
||||
context.textAlign = overflowLeft ? "start" : "end";
|
||||
|
||||
// Text x / y
|
||||
const lx = overflowLeft ? -bw + 6 : -6;
|
||||
const ly = overflowBottom ? -bh - 6 : 16;
|
||||
|
||||
// Label background x / y
|
||||
const lbx = overflowLeft ? bw - tw - 12 : 0;
|
||||
const lby = overflowBottom ? bh : -24;
|
||||
|
||||
// Draw background
|
||||
context.fillRect(lbx, lby, tw+8, th);
|
||||
|
||||
// Draw text, rotated back upright (canvas draws rotated 180deg)
|
||||
context.fillStyle = "white";
|
||||
context.rotate(Math.PI);
|
||||
context.fillText(s.Name, lx, ly);
|
||||
|
||||
// reset transform
|
||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@if (Model.PageNumber != 0)
|
||||
{
|
||||
<a href="/reports/@(Model.PageNumber - 1)@(Model.SearchValue.Length == 0 ? "" : "?name=" + Model.SearchValue)">Previous Page</a>
|
||||
}
|
||||
@(Model.PageNumber + 1) / @(Model.PageAmount)
|
||||
@if (Model.PageNumber < Model.PageAmount - 1)
|
||||
{
|
||||
<a href="/reports/@(Model.PageNumber + 1)@(Model.SearchValue.Length == 0 ? "" : "?name=" + Model.SearchValue)">Next Page</a>
|
||||
}
|
65
ProjectLighthouse/Pages/ReportsPage.cshtml.cs
Normal file
65
ProjectLighthouse/Pages/ReportsPage.cshtml.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using LBPUnion.ProjectLighthouse.Pages.Layouts;
|
||||
using LBPUnion.ProjectLighthouse.Types.Reports;
|
||||
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Pages;
|
||||
|
||||
public class ReportsPage : BaseLayout
|
||||
{
|
||||
|
||||
public int PageAmount;
|
||||
|
||||
public int PageNumber;
|
||||
|
||||
public int ReportCount;
|
||||
|
||||
public List<GriefReport> Reports;
|
||||
|
||||
public string SearchValue;
|
||||
|
||||
public ReportsPage([NotNull] Database database) : base(database)
|
||||
{ }
|
||||
|
||||
public async Task<IActionResult> OnGet([FromRoute] int pageNumber, [FromQuery] string? name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name)) name = "";
|
||||
|
||||
this.SearchValue = name.Replace(" ", string.Empty);
|
||||
|
||||
this.ReportCount = await this.Database.Reports.Include(r => r.ReportingPlayer).CountAsync(r => r.ReportingPlayer.Username.Contains(this.SearchValue));
|
||||
|
||||
this.PageNumber = pageNumber;
|
||||
this.PageAmount = Math.Max(1, (int) Math.Ceiling((double) this.ReportCount / ServerStatics.PageSize));
|
||||
|
||||
if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount)
|
||||
return this.Redirect($"/reports/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}");
|
||||
|
||||
this.Reports = await this.Database.Reports.Include(r => r.ReportingPlayer)
|
||||
.Where(r => r.ReportingPlayer.Username.Contains(this.SearchValue))
|
||||
.OrderByDescending(r => r.Timestamp)
|
||||
.Skip(pageNumber * ServerStatics.PageSize)
|
||||
.Take(ServerStatics.PageSize)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (GriefReport r in this.Reports)
|
||||
{
|
||||
r.XmlPlayers = (ReportPlayer[]) JsonSerializer.Deserialize(r.Players, typeof(ReportPlayer[]))!;
|
||||
|
||||
r.XmlBounds = new Marqee()
|
||||
{
|
||||
Rect = (Rectangle) JsonSerializer.Deserialize(r.Bounds, typeof(Rectangle))!,
|
||||
};
|
||||
}
|
||||
|
||||
return this.Page();
|
||||
}
|
||||
}
|
|
@ -1,8 +1,5 @@
|
|||
@page "/slot/{id:int}"
|
||||
@using System.IO
|
||||
@using System.Web
|
||||
@using LBPUnion.ProjectLighthouse.Helpers.Extensions
|
||||
@using LBPUnion.ProjectLighthouse.Types.Profiles
|
||||
@model LBPUnion.ProjectLighthouse.Pages.SlotPage
|
||||
|
||||
@{
|
||||
|
@ -57,36 +54,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui yellow segment">
|
||||
<h1>Comments</h1>
|
||||
@if (Model.Comments.Count == 0)
|
||||
{
|
||||
<p>There are no comments.</p>
|
||||
}
|
||||
|
||||
@foreach (Comment comment in Model.Comments!)
|
||||
{
|
||||
DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(comment.Timestamp / 1000);
|
||||
StringWriter messageWriter = new();
|
||||
HttpUtility.HtmlDecode(comment.getComment(), messageWriter);
|
||||
string decodedMessage = messageWriter.ToString();
|
||||
<div>
|
||||
<b><a href="/user/@comment.PosterUserId">@comment.Poster.Username</a>: </b>
|
||||
@if (comment.Deleted)
|
||||
{
|
||||
<i><span>@decodedMessage</span></i>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@decodedMessage</span>
|
||||
}
|
||||
<p>
|
||||
<i>@timestamp.ToString("MM/dd/yyyy @ h:mm tt") UTC</i>
|
||||
</p>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@await Html.PartialAsync("Partials/CommentsPartial")
|
||||
|
||||
@if (Model.User != null && Model.User.IsAdmin)
|
||||
{
|
||||
<div class="ui yellow segment">
|
||||
|
|
|
@ -7,6 +7,7 @@ using LBPUnion.ProjectLighthouse.Pages.Layouts;
|
|||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
|
@ -16,6 +17,8 @@ public class SlotPage : BaseLayout
|
|||
{
|
||||
public List<Comment> Comments;
|
||||
|
||||
public bool CommentsEnabled = ServerSettings.Instance.LevelCommentsEnabled;
|
||||
|
||||
public Slot Slot;
|
||||
public SlotPage([NotNull] Database database) : base(database)
|
||||
{}
|
||||
|
@ -33,6 +36,15 @@ public class SlotPage : BaseLayout
|
|||
|
||||
this.Slot = slot;
|
||||
|
||||
if (this.User == null) return this.Page();
|
||||
|
||||
foreach (Comment c in this.Comments)
|
||||
{
|
||||
Reaction? reaction = await this.Database.Reactions.FirstOrDefaultAsync(r =>
|
||||
r.UserId == this.User.UserId && r.TargetId == c.CommentId);
|
||||
if (reaction != null) c.YourThumb = reaction.Rating;
|
||||
}
|
||||
|
||||
return this.Page();
|
||||
}
|
||||
}
|
|
@ -32,16 +32,17 @@ public class SlotsPage : BaseLayout
|
|||
{
|
||||
if (string.IsNullOrWhiteSpace(name)) name = "";
|
||||
|
||||
this.SlotCount = await this.Database.Slots.CountAsync(p => p.Name.Contains(name));
|
||||
this.SearchValue = name.Replace(" ", string.Empty);
|
||||
|
||||
this.SlotCount = await this.Database.Slots.CountAsync(p => p.Name.Contains(this.SearchValue));
|
||||
|
||||
this.SearchValue = name;
|
||||
this.PageNumber = pageNumber;
|
||||
this.PageAmount = Math.Max(1, (int)Math.Ceiling((double)this.SlotCount / ServerStatics.PageSize));
|
||||
|
||||
if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) return this.Redirect($"/slots/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}");
|
||||
|
||||
this.Slots = await this.Database.Slots.Include(p => p.Creator)
|
||||
.Where(p => p.Name.Contains(name))
|
||||
.Where(p => p.Name.Contains(this.SearchValue))
|
||||
.OrderByDescending(p => p.FirstUploaded)
|
||||
.Skip(pageNumber * ServerStatics.PageSize)
|
||||
.Take(ServerStatics.PageSize)
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
@page "/user/{userId:int}"
|
||||
@using System.IO
|
||||
@using System.Web
|
||||
@using LBPUnion.ProjectLighthouse.Helpers.Extensions
|
||||
@using LBPUnion.ProjectLighthouse.Types
|
||||
@using LBPUnion.ProjectLighthouse.Types.Profiles
|
||||
@model LBPUnion.ProjectLighthouse.Pages.UserPage
|
||||
|
||||
@{
|
||||
|
@ -114,35 +111,4 @@
|
|||
</div>
|
||||
}
|
||||
|
||||
|
||||
<div class="ui yellow segment">
|
||||
<h1>Comments</h1>
|
||||
@if (Model.ProfileUser.Comments == 0)
|
||||
{
|
||||
<p>There are no comments.</p>
|
||||
}
|
||||
|
||||
@foreach (Comment comment in Model.Comments!)
|
||||
{
|
||||
DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(comment.Timestamp / 1000);
|
||||
StringWriter messageWriter = new();
|
||||
HttpUtility.HtmlDecode(comment.getComment(), messageWriter);
|
||||
string decodedMessage = messageWriter.ToString();
|
||||
<div>
|
||||
<b><a href="/user/@comment.PosterUserId">@comment.Poster.Username</a>: </b>
|
||||
@if (comment.Deleted)
|
||||
{
|
||||
<i><span>@decodedMessage</span></i>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@decodedMessage</span>
|
||||
}
|
||||
|
||||
<p>
|
||||
<i>@timestamp.ToString("MM/dd/yyyy @ h:mm tt") UTC</i>
|
||||
</p>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@await Html.PartialAsync("Partials/CommentsPartial")
|
|
@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
|||
using LBPUnion.ProjectLighthouse.Pages.Layouts;
|
||||
using LBPUnion.ProjectLighthouse.Types;
|
||||
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
|
@ -14,6 +15,8 @@ public class UserPage : BaseLayout
|
|||
{
|
||||
public List<Comment>? Comments;
|
||||
|
||||
public bool CommentsEnabled = ServerSettings.Instance.ProfileCommentsEnabled;
|
||||
|
||||
public bool IsProfileUserHearted;
|
||||
|
||||
public List<Photo>? Photos;
|
||||
|
@ -34,11 +37,19 @@ public class UserPage : BaseLayout
|
|||
.Where(p => p.TargetId == userId && p.Type == CommentType.Profile)
|
||||
.Take(50)
|
||||
.ToListAsync();
|
||||
|
||||
if (this.User != null)
|
||||
this.IsProfileUserHearted = await this.Database.HeartedProfiles.FirstOrDefaultAsync
|
||||
(u => u.UserId == this.User.UserId && u.HeartedUserId == this.ProfileUser.UserId) !=
|
||||
{
|
||||
foreach (Comment c in this.Comments)
|
||||
{
|
||||
Reaction? reaction = await this.Database.Reactions.FirstOrDefaultAsync(r =>
|
||||
r.UserId == this.User.UserId && r.TargetId == c.CommentId);
|
||||
if (reaction != null) c.YourThumb = reaction.Rating;
|
||||
}
|
||||
this.IsProfileUserHearted = await this.Database.HeartedProfiles.FirstOrDefaultAsync(u =>
|
||||
u.UserId == this.User.UserId &&
|
||||
u.HeartedUserId == this.ProfileUser.UserId) !=
|
||||
null;
|
||||
}
|
||||
|
||||
return this.Page();
|
||||
}
|
||||
|
|
|
@ -31,15 +31,16 @@ public class UsersPage : BaseLayout
|
|||
{
|
||||
if (string.IsNullOrWhiteSpace(name)) name = "";
|
||||
|
||||
this.UserCount = await this.Database.Users.CountAsync(u => !u.Banned && u.Username.Contains(name));
|
||||
this.SearchValue = name.Replace(" ", string.Empty);
|
||||
|
||||
this.UserCount = await this.Database.Users.CountAsync(u => !u.Banned && u.Username.Contains(this.SearchValue));
|
||||
|
||||
this.SearchValue = name;
|
||||
this.PageNumber = pageNumber;
|
||||
this.PageAmount = Math.Max(1, (int)Math.Ceiling((double)this.UserCount / ServerStatics.PageSize));
|
||||
|
||||
if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) return this.Redirect($"/users/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}");
|
||||
|
||||
this.Users = await this.Database.Users.Where(u => !u.Banned && u.Username.Contains(name))
|
||||
this.Users = await this.Database.Users.Where(u => !u.Banned && u.Username.Contains(this.SearchValue))
|
||||
.OrderByDescending(b => b.UserId)
|
||||
.Skip(pageNumber * ServerStatics.PageSize)
|
||||
.Take(ServerStatics.PageSize)
|
||||
|
|
|
@ -100,7 +100,7 @@ public static class Program
|
|||
{
|
||||
while (fileQueue.TryDequeue(out string? filename))
|
||||
{
|
||||
LbpFile? file = LbpFile.FromHash(filename.Replace("r/", ""));
|
||||
LbpFile? file = LbpFile.FromHash(filename.Replace("r" + Path.DirectorySeparatorChar, ""));
|
||||
if (file == null) continue;
|
||||
|
||||
if (file.FileType == LbpFileType.Jpeg || file.FileType == LbpFileType.Png || file.FileType == LbpFileType.Texture)
|
||||
|
|
|
@ -37,6 +37,10 @@ public class Comment
|
|||
public int ThumbsUp { get; set; }
|
||||
public int ThumbsDown { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
[XmlIgnore]
|
||||
public int YourThumb;
|
||||
|
||||
public string getComment()
|
||||
{
|
||||
if (!this.Deleted)
|
||||
|
|
59
ProjectLighthouse/Types/Reports/GriefReport.cs
Normal file
59
ProjectLighthouse/Types/Reports/GriefReport.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Reports;
|
||||
|
||||
[XmlRoot("griefReport")]
|
||||
public class GriefReport
|
||||
{
|
||||
[Key]
|
||||
public int ReportId { get; set; }
|
||||
|
||||
[XmlElement("griefTypeId")]
|
||||
public GriefType Type { get; set; }
|
||||
|
||||
public long Timestamp { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
[XmlElement("visibleBadge")]
|
||||
public VisiblePlayer[] XmlVisiblePlayers { get; set; }
|
||||
|
||||
public string VisiblePlayers { get; set; }
|
||||
|
||||
public int ReportingPlayerId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(ReportingPlayerId))]
|
||||
public User ReportingPlayer { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
[XmlElement("player")]
|
||||
public ReportPlayer[] XmlPlayers { get; set; }
|
||||
|
||||
public string Players { get; set; }
|
||||
|
||||
[XmlElement("griefStateHash")]
|
||||
public string GriefStateHash { get; set; }
|
||||
|
||||
[XmlElement("levelOwner")]
|
||||
public string LevelOwner { get; set; }
|
||||
|
||||
[XmlElement("initialStateHash")]
|
||||
public string InitialStateHash { get; set; }
|
||||
|
||||
[XmlElement("jpegHash")]
|
||||
public string JpegHash { get; set; }
|
||||
|
||||
[XmlElement("levelId")]
|
||||
public int LevelId { get; set; }
|
||||
|
||||
[XmlElement("levelType")]
|
||||
public string LevelType { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
[XmlElement("marqee")]
|
||||
public Marqee XmlBounds { get; set; }
|
||||
|
||||
public string Bounds { get; set; }
|
||||
|
||||
}
|
23
ProjectLighthouse/Types/Reports/GriefType.cs
Normal file
23
ProjectLighthouse/Types/Reports/GriefType.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Reports;
|
||||
|
||||
public enum GriefType
|
||||
{
|
||||
[XmlEnum("1")]
|
||||
Obscene = 1,
|
||||
[XmlEnum("2")]
|
||||
Mature = 2,
|
||||
[XmlEnum("3")]
|
||||
Offensive = 3,
|
||||
[XmlEnum("4")]
|
||||
Violence = 4,
|
||||
[XmlEnum("5")]
|
||||
Illegal = 5,
|
||||
[XmlEnum("6")]
|
||||
Unknown = 6,
|
||||
[XmlEnum("7")]
|
||||
Tos = 7,
|
||||
[XmlEnum("8")]
|
||||
Other = 8,
|
||||
}
|
10
ProjectLighthouse/Types/Reports/Marqee.cs
Normal file
10
ProjectLighthouse/Types/Reports/Marqee.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Reports;
|
||||
|
||||
[XmlRoot("marqee")]
|
||||
public class Marqee
|
||||
{
|
||||
[XmlElement("rect")]
|
||||
public Rectangle Rect { get; set; }
|
||||
}
|
23
ProjectLighthouse/Types/Reports/Rectangle.cs
Normal file
23
ProjectLighthouse/Types/Reports/Rectangle.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Reports;
|
||||
|
||||
public class Rectangle
|
||||
{
|
||||
[XmlAttribute("t")]
|
||||
public int Top { get; set; }
|
||||
|
||||
[XmlAttribute("l")]
|
||||
public int Left { get; set; }
|
||||
|
||||
[XmlAttribute("b")]
|
||||
public int Bottom { get; set; }
|
||||
|
||||
[XmlAttribute("r")]
|
||||
public int Right { get; set; }
|
||||
|
||||
}
|
23
ProjectLighthouse/Types/Reports/ReportPlayer.cs
Normal file
23
ProjectLighthouse/Types/Reports/ReportPlayer.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Reports;
|
||||
|
||||
[XmlRoot("player")]
|
||||
public class ReportPlayer
|
||||
{
|
||||
[XmlElement("id")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[XmlElement("rect")]
|
||||
public Rectangle Location { get; set; }
|
||||
|
||||
[XmlAttribute("reporter")]
|
||||
public bool Reporter { get; set; }
|
||||
|
||||
[XmlAttribute("ingamenow")]
|
||||
public bool InGame { get; set; }
|
||||
|
||||
[XmlAttribute("playerNumber")]
|
||||
public int PlayerNum { get; set; }
|
||||
|
||||
}
|
22
ProjectLighthouse/Types/Reports/VisiblePlayer.cs
Normal file
22
ProjectLighthouse/Types/Reports/VisiblePlayer.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Reports;
|
||||
|
||||
[XmlRoot("visibleBadge")]
|
||||
[Serializable]
|
||||
public class VisiblePlayer
|
||||
{
|
||||
[XmlElement("id")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[XmlElement("hash")]
|
||||
public string Hash { get; set; }
|
||||
|
||||
[XmlElement("rect")]
|
||||
public Rectangle Bounds { get; set; }
|
||||
|
||||
[XmlAttribute("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
}
|
|
@ -190,8 +190,8 @@ public class User
|
|||
LbpSerializer.BlankElement("photos") +
|
||||
LbpSerializer.StringElement("heartCount", this.Hearts) +
|
||||
LbpSerializer.StringElement("yay2", this.YayHash) +
|
||||
LbpSerializer.StringElement("boo2", this.YayHash) +
|
||||
LbpSerializer.StringElement("meh2", this.YayHash);
|
||||
LbpSerializer.StringElement("boo2", this.BooHash) +
|
||||
LbpSerializer.StringElement("meh2", this.MehHash);
|
||||
this.ClientsConnected.Serialize();
|
||||
|
||||
return LbpSerializer.TaggedStringElement("user", user, "type", "user");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue