mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-07-31 09:18:38 +00:00
Add level comments and yays and boos for all comments
This commit is contained in:
parent
075bef6d68
commit
19a29ca328
15 changed files with 370 additions and 46 deletions
|
@ -1,4 +1,5 @@
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -7,6 +8,7 @@ using System.Xml.Serialization;
|
||||||
using LBPUnion.ProjectLighthouse.Helpers;
|
using LBPUnion.ProjectLighthouse.Helpers;
|
||||||
using LBPUnion.ProjectLighthouse.Serialization;
|
using LBPUnion.ProjectLighthouse.Serialization;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
|
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
@ -24,37 +26,130 @@ public class CommentController : ControllerBase
|
||||||
this.database = database;
|
this.database = database;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("userComments/{username}")]
|
[HttpPost("rateUserComment/{username}")]
|
||||||
public async Task<IActionResult> GetComments(string username)
|
[HttpPost("rateComment/user/{slotId:int}")]
|
||||||
|
public async Task<IActionResult> RateComment([FromQuery] int commentId, [FromQuery] int rating, string? username, int? slotId)
|
||||||
{
|
{
|
||||||
List<Comment> comments = await this.database.Comments.Include
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
(c => c.Target)
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
Comment? comment = await this.database.Comments.Include(c => c.Poster).FirstOrDefaultAsync(c => commentId == c.CommentId);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet("comments/user/{slotId:int}")]
|
||||||
|
[HttpGet("userComments/{username}")]
|
||||||
|
public async Task<IActionResult> GetComments([FromQuery] int pageStart, [FromQuery] int pageSize, string? username, int? slotId)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
int targetId = slotId.GetValueOrDefault();
|
||||||
|
CommentType type = CommentType.Level;
|
||||||
|
if (!string.IsNullOrWhiteSpace(username))
|
||||||
|
{
|
||||||
|
targetId = this.database.Users.First(u => u.Username.Equals(username)).UserId;
|
||||||
|
type = CommentType.Profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Comment> comments = await this.database.Comments
|
||||||
.Include(c => c.Poster)
|
.Include(c => c.Poster)
|
||||||
.Where(c => c.Target.Username == username)
|
.Where(c => c.TargetId == targetId && c.Type == type)
|
||||||
.OrderByDescending(c => c.Timestamp)
|
.OrderByDescending(c => c.Timestamp)
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(Math.Min(pageSize,
|
||||||
|
30))
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
string outputXml = comments.Aggregate(string.Empty, (current, comment) => current + comment.Serialize());
|
string outputXml = comments.Aggregate(string.Empty, (current, comment) => current +
|
||||||
|
comment.Serialize(this.getReaction(user.UserId, comment.CommentId).Result));
|
||||||
return this.Ok(LbpSerializer.StringElement("comments", outputXml));
|
return this.Ok(LbpSerializer.StringElement("comments", outputXml));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<int> getReaction(int userId, int commentId)
|
||||||
|
{
|
||||||
|
Reaction? reaction = await this.database.Reactions.FirstOrDefaultAsync(r => r.UserId == userId && r.TargetId == commentId);
|
||||||
|
if (reaction == null) return 0;
|
||||||
|
return reaction.Rating;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("postUserComment/{username}")]
|
[HttpPost("postUserComment/{username}")]
|
||||||
public async Task<IActionResult> PostComment(string username)
|
[HttpPost("postComment/user/{slotId:int}")]
|
||||||
|
public async Task<IActionResult> PostComment(string? username, int? slotId)
|
||||||
{
|
{
|
||||||
this.Request.Body.Position = 0;
|
this.Request.Body.Position = 0;
|
||||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||||
|
|
||||||
XmlSerializer serializer = new(typeof(Comment));
|
XmlSerializer serializer = new(typeof(Comment));
|
||||||
Comment? comment = (Comment?)serializer.Deserialize(new StringReader(bodyString));
|
Comment? comment = (Comment?) serializer.Deserialize(new StringReader(bodyString));
|
||||||
|
|
||||||
|
CommentType type = (slotId.GetValueOrDefault() == 0 ? CommentType.Profile : CommentType.Level);
|
||||||
|
|
||||||
User? poster = await this.database.UserFromGameRequest(this.Request);
|
User? poster = await this.database.UserFromGameRequest(this.Request);
|
||||||
if (poster == null) return this.StatusCode(403, "");
|
if (poster == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
User? target = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
if (comment == null) return this.BadRequest();
|
||||||
if (comment == null || target == null) return this.BadRequest();
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
comment.PosterUserId = poster.UserId;
|
comment.PosterUserId = poster.UserId;
|
||||||
comment.TargetUserId = target.UserId;
|
comment.TargetId = targetId;
|
||||||
|
comment.Type = type;
|
||||||
|
|
||||||
comment.Timestamp = TimeHelper.UnixTimeMilliseconds();
|
comment.Timestamp = TimeHelper.UnixTimeMilliseconds();
|
||||||
|
|
||||||
|
@ -64,17 +159,40 @@ public class CommentController : ControllerBase
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("deleteUserComment/{username}")]
|
[HttpPost("deleteUserComment/{username}")]
|
||||||
public async Task<IActionResult> DeleteComment([FromQuery] int commentId, string username)
|
[HttpPost("deleteComment/user/{slotId:int}")]
|
||||||
|
public async Task<IActionResult> DeleteComment([FromQuery] int commentId, string? username, int? slotId)
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
if (user == null) return this.StatusCode(403, "");
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId);
|
Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId);
|
||||||
if (comment == null) return this.NotFound();
|
if (comment == null) return this.NotFound();
|
||||||
|
// if you are not the poster
|
||||||
|
if (comment.PosterUserId != user.UserId)
|
||||||
|
{
|
||||||
|
if (comment.Type == CommentType.Profile)
|
||||||
|
{
|
||||||
|
// if you aren't the poster and aren't the profile owner
|
||||||
|
if (comment.TargetId != user.UserId)
|
||||||
|
{
|
||||||
|
return this.StatusCode(403, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == comment.TargetId);
|
||||||
|
// if you aren't the creator of the level
|
||||||
|
if (slot == null || slot.CreatorId != user.UserId || slotId.GetValueOrDefault() != slot.SlotId)
|
||||||
|
{
|
||||||
|
return this.StatusCode(403, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (comment.TargetUserId != user.UserId && comment.PosterUserId != user.UserId) return this.StatusCode(403, "");
|
comment.Deleted = true;
|
||||||
|
comment.DeletedBy = user.Username;
|
||||||
|
comment.DeletedType = "user";
|
||||||
|
|
||||||
this.database.Comments.Remove(comment);
|
|
||||||
await this.database.SaveChangesAsync();
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
return this.Ok();
|
return this.Ok();
|
||||||
|
|
|
@ -36,6 +36,7 @@ public class Database : DbContext
|
||||||
public DbSet<RatedReview> RatedReviews { get; set; }
|
public DbSet<RatedReview> RatedReviews { get; set; }
|
||||||
public DbSet<UserApprovedIpAddress> UserApprovedIpAddresses { get; set; }
|
public DbSet<UserApprovedIpAddress> UserApprovedIpAddresses { get; set; }
|
||||||
public DbSet<DatabaseCategory> CustomCategories { get; set; }
|
public DbSet<DatabaseCategory> CustomCategories { get; set; }
|
||||||
|
public DbSet<Reaction> Reactions { get; set; }
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||||
=> options.UseMySql(ServerSettings.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
|
=> options.UseMySql(ServerSettings.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
|
||||||
|
@ -272,6 +273,7 @@ public class Database : DbContext
|
||||||
this.Comments.RemoveRange(this.Comments.Where(c => c.PosterUserId == user.UserId));
|
this.Comments.RemoveRange(this.Comments.Where(c => c.PosterUserId == user.UserId));
|
||||||
this.Reviews.RemoveRange(this.Reviews.Where(r => r.ReviewerId == user.UserId));
|
this.Reviews.RemoveRange(this.Reviews.Where(r => r.ReviewerId == user.UserId));
|
||||||
this.Photos.RemoveRange(this.Photos.Where(p => p.CreatorId == user.UserId));
|
this.Photos.RemoveRange(this.Photos.Where(p => p.CreatorId == user.UserId));
|
||||||
|
this.Reactions.RemoveRange(this.Reactions.Where(p => p.UserId == user.UserId));
|
||||||
|
|
||||||
this.Users.Remove(user);
|
this.Users.Remove(user);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ProjectLighthouse.Migrations
|
||||||
|
{
|
||||||
|
public partial class CommentRefactor : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Comments_Slots_TargetId",
|
||||||
|
table: "Comments");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Comments_Users_TargetId",
|
||||||
|
table: "Comments");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Comments_TargetId",
|
||||||
|
table: "Comments");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Comments_TargetId",
|
||||||
|
table: "Comments",
|
||||||
|
column: "TargetId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Comments_Slots_TargetId",
|
||||||
|
table: "Comments",
|
||||||
|
column: "TargetId",
|
||||||
|
principalTable: "Slots",
|
||||||
|
principalColumn: "SlotId",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Comments_Users_TargetId",
|
||||||
|
table: "Comments",
|
||||||
|
column: "TargetId",
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "UserId",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -412,13 +412,22 @@ namespace ProjectLighthouse.Migrations
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<bool>("Deleted")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("DeletedBy")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("DeletedType")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<string>("Message")
|
b.Property<string>("Message")
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<int>("PosterUserId")
|
b.Property<int>("PosterUserId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<int>("TargetUserId")
|
b.Property<int>("TargetId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<int>("ThumbsDown")
|
b.Property<int>("ThumbsDown")
|
||||||
|
@ -430,12 +439,13 @@ namespace ProjectLighthouse.Migrations
|
||||||
b.Property<long>("Timestamp")
|
b.Property<long>("Timestamp")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.HasKey("CommentId");
|
b.HasKey("CommentId");
|
||||||
|
|
||||||
b.HasIndex("PosterUserId");
|
b.HasIndex("PosterUserId");
|
||||||
|
|
||||||
b.HasIndex("TargetUserId");
|
|
||||||
|
|
||||||
b.ToTable("Comments");
|
b.ToTable("Comments");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -473,6 +483,26 @@ namespace ProjectLighthouse.Migrations
|
||||||
b.ToTable("Locations");
|
b.ToTable("Locations");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reaction", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("RatingId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Rating")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("TargetId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("RatingId");
|
||||||
|
|
||||||
|
b.ToTable("Reactions");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b =>
|
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("RatedReviewId")
|
b.Property<int>("RatedReviewId")
|
||||||
|
@ -829,15 +859,7 @@ namespace ProjectLighthouse.Migrations
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("TargetUserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Poster");
|
b.Navigation("Poster");
|
||||||
|
|
||||||
b.Navigation("Target");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b =>
|
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b =>
|
||||||
|
|
|
@ -10,11 +10,13 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function onSubmit(form) {
|
function onSubmit(form) {
|
||||||
const password = form['password'];
|
const passwordInput = document.getElementById("password");
|
||||||
const confirmPassword = form['confirmPassword'];
|
const confirmPasswordInput = document.getElementById("confirmPassword");
|
||||||
|
const passwordSubmit = document.getElementById("password-submit");
|
||||||
|
const confirmPasswordSubmit = document.getElementById("confirmPassword-submit");
|
||||||
|
|
||||||
password.value = sha256(password.value);
|
passwordSubmit.value = sha256(passwordInput.value);
|
||||||
confirmPassword.value = sha256(confirmPassword.value);
|
confirmPasswordSubmit.value = sha256(confirmPasswordInput.value);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -35,12 +37,14 @@
|
||||||
|
|
||||||
<div class="ui left labeled input">
|
<div class="ui left labeled input">
|
||||||
<label for="password" class="ui blue label">Password: </label>
|
<label for="password" class="ui blue label">Password: </label>
|
||||||
<input type="password" name="password" id="password">
|
<input type="password" id="password">
|
||||||
|
<input type="hidden" id="password-submit" name="password">
|
||||||
</div><br><br>
|
</div><br><br>
|
||||||
|
|
||||||
<div class="ui left labeled input">
|
<div class="ui left labeled input">
|
||||||
<label for="password" class="ui blue label">Confirm Password: </label>
|
<label for="password" class="ui blue label">Confirm Password: </label>
|
||||||
<input type="password" name="confirmPassword" id="confirmPassword">
|
<input type="password" id="confirmPassword">
|
||||||
|
<input type="hidden" id="confirmPassword-submit" name="confirmPassword">
|
||||||
</div><br><br><br>
|
</div><br><br><br>
|
||||||
|
|
||||||
<input type="submit" value="Reset password and continue" id="submit" class="ui green button"><br>
|
<input type="submit" value="Reset password and continue" id="submit" class="ui green button"><br>
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
@page "/slot/{id:int}"
|
@page "/slot/{id:int}"
|
||||||
|
@using System.IO
|
||||||
|
@using System.Web
|
||||||
@using LBPUnion.ProjectLighthouse.Helpers.Extensions
|
@using LBPUnion.ProjectLighthouse.Helpers.Extensions
|
||||||
|
@using LBPUnion.ProjectLighthouse.Types.Profiles
|
||||||
@model LBPUnion.ProjectLighthouse.Pages.SlotPage
|
@model LBPUnion.ProjectLighthouse.Pages.SlotPage
|
||||||
|
|
||||||
@{
|
@{
|
||||||
|
@ -54,6 +57,36 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
||||||
@if (Model.User != null && Model.User.IsAdmin)
|
@if (Model.User != null && Model.User.IsAdmin)
|
||||||
{
|
{
|
||||||
<div class="ui yellow segment">
|
<div class="ui yellow segment">
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using LBPUnion.ProjectLighthouse.Pages.Layouts;
|
using LBPUnion.ProjectLighthouse.Pages.Layouts;
|
||||||
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
|
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
@ -10,6 +14,7 @@ namespace LBPUnion.ProjectLighthouse.Pages;
|
||||||
|
|
||||||
public class SlotPage : BaseLayout
|
public class SlotPage : BaseLayout
|
||||||
{
|
{
|
||||||
|
public List<Comment> Comments;
|
||||||
|
|
||||||
public Slot Slot;
|
public Slot Slot;
|
||||||
public SlotPage([NotNull] Database database) : base(database)
|
public SlotPage([NotNull] Database database) : base(database)
|
||||||
|
@ -20,6 +25,12 @@ public class SlotPage : BaseLayout
|
||||||
Slot? slot = await this.Database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id);
|
Slot? slot = await this.Database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
if (slot == null) return this.NotFound();
|
if (slot == null) return this.NotFound();
|
||||||
|
|
||||||
|
this.Comments = await this.Database.Comments.Include(p => p.Poster)
|
||||||
|
.OrderByDescending(p => p.Timestamp)
|
||||||
|
.Where(c => c.TargetId == id && c.Type == CommentType.Level)
|
||||||
|
.Take(50)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
this.Slot = slot;
|
this.Slot = slot;
|
||||||
|
|
||||||
return this.Page();
|
return this.Page();
|
||||||
|
|
|
@ -126,11 +126,19 @@
|
||||||
{
|
{
|
||||||
DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(comment.Timestamp / 1000);
|
DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(comment.Timestamp / 1000);
|
||||||
StringWriter messageWriter = new();
|
StringWriter messageWriter = new();
|
||||||
HttpUtility.HtmlDecode(comment.Message, messageWriter);
|
HttpUtility.HtmlDecode(comment.getComment(), messageWriter);
|
||||||
string decodedMessage = messageWriter.ToString();
|
string decodedMessage = messageWriter.ToString();
|
||||||
<div>
|
<div>
|
||||||
<b><a href="/user/@comment.PosterUserId">@comment.Poster.Username</a>: </b>
|
<b><a href="/user/@comment.PosterUserId">@comment.Poster.Username</a>: </b>
|
||||||
<span>@decodedMessage</span>
|
@if (comment.Deleted)
|
||||||
|
{
|
||||||
|
<i><span>@decodedMessage</span></i>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>@decodedMessage</span>
|
||||||
|
}
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<i>@timestamp.ToString("MM/dd/yyyy @ h:mm tt") UTC</i>
|
<i>@timestamp.ToString("MM/dd/yyyy @ h:mm tt") UTC</i>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -30,9 +30,8 @@ public class UserPage : BaseLayout
|
||||||
this.Photos = await this.Database.Photos.OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(6).ToListAsync();
|
this.Photos = await this.Database.Photos.OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(6).ToListAsync();
|
||||||
this.Comments = await this.Database.Comments.Include
|
this.Comments = await this.Database.Comments.Include
|
||||||
(p => p.Poster)
|
(p => p.Poster)
|
||||||
.Include(p => p.Target)
|
|
||||||
.OrderByDescending(p => p.Timestamp)
|
.OrderByDescending(p => p.Timestamp)
|
||||||
.Where(p => p.TargetUserId == userId)
|
.Where(p => p.TargetId == userId && p.Type == CommentType.Profile)
|
||||||
.Take(50)
|
.Take(50)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
|
7
ProjectLighthouse/Types/CommentType.cs
Normal file
7
ProjectLighthouse/Types/CommentType.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace LBPUnion.ProjectLighthouse.Types;
|
||||||
|
|
||||||
|
public enum CommentType
|
||||||
|
{
|
||||||
|
Profile = 0,
|
||||||
|
Level = 1,
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ using LBPUnion.ProjectLighthouse.Helpers;
|
||||||
using LBPUnion.ProjectLighthouse.Serialization;
|
using LBPUnion.ProjectLighthouse.Serialization;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Reviews;
|
using LBPUnion.ProjectLighthouse.Types.Reviews;
|
||||||
|
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Types.Levels;
|
namespace LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
|
|
||||||
|
@ -121,6 +122,19 @@ public class Slot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[XmlIgnore]
|
||||||
|
[NotMapped]
|
||||||
|
[JsonIgnore]
|
||||||
|
public int Comments
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
using Database database = new();
|
||||||
|
|
||||||
|
return database.Comments.Count(c => c.Type == CommentType.Level && c.TargetId == this.SlotId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public int Plays => this.PlaysLBP1 + this.PlaysLBP2 + this.PlaysLBP3 + this.PlaysLBPVita;
|
public int Plays => this.PlaysLBP1 + this.PlaysLBP2 + this.PlaysLBP3 + this.PlaysLBPVita;
|
||||||
|
@ -251,6 +265,7 @@ public class Slot
|
||||||
LbpSerializer.StringElement("icon", this.IconHash) +
|
LbpSerializer.StringElement("icon", this.IconHash) +
|
||||||
LbpSerializer.StringElement("rootLevel", this.RootLevel) +
|
LbpSerializer.StringElement("rootLevel", this.RootLevel) +
|
||||||
LbpSerializer.StringElement("authorLabels", this.AuthorLabels) +
|
LbpSerializer.StringElement("authorLabels", this.AuthorLabels) +
|
||||||
|
LbpSerializer.StringElement("labels", this.AuthorLabels) +
|
||||||
this.SerializeResources() +
|
this.SerializeResources() +
|
||||||
LbpSerializer.StringElement("location", this.Location?.Serialize()) +
|
LbpSerializer.StringElement("location", this.Location?.Serialize()) +
|
||||||
LbpSerializer.StringElement("initiallyLocked", this.InitiallyLocked) +
|
LbpSerializer.StringElement("initiallyLocked", this.InitiallyLocked) +
|
||||||
|
@ -266,6 +281,7 @@ public class Slot
|
||||||
LbpSerializer.StringElement("mmpick", this.TeamPick) +
|
LbpSerializer.StringElement("mmpick", this.TeamPick) +
|
||||||
LbpSerializer.StringElement("heartCount", this.Hearts) +
|
LbpSerializer.StringElement("heartCount", this.Hearts) +
|
||||||
LbpSerializer.StringElement("playCount", this.Plays) +
|
LbpSerializer.StringElement("playCount", this.Plays) +
|
||||||
|
LbpSerializer.StringElement("commentCount", this.Comments) +
|
||||||
LbpSerializer.StringElement("uniquePlayCount", this.PlaysLBP2Unique) + // ??? good naming scheme lol
|
LbpSerializer.StringElement("uniquePlayCount", this.PlaysLBP2Unique) + // ??? good naming scheme lol
|
||||||
LbpSerializer.StringElement("completionCount", this.PlaysComplete) +
|
LbpSerializer.StringElement("completionCount", this.PlaysComplete) +
|
||||||
LbpSerializer.StringElement("lbp1PlayCount", this.PlaysLBP1) +
|
LbpSerializer.StringElement("lbp1PlayCount", this.PlaysLBP1) +
|
||||||
|
@ -277,7 +293,7 @@ public class Slot
|
||||||
LbpSerializer.StringElement("lbp3PlayCount", this.PlaysLBP3) +
|
LbpSerializer.StringElement("lbp3PlayCount", this.PlaysLBP3) +
|
||||||
LbpSerializer.StringElement("lbp3CompletionCount", this.PlaysLBP3Complete) +
|
LbpSerializer.StringElement("lbp3CompletionCount", this.PlaysLBP3Complete) +
|
||||||
LbpSerializer.StringElement("lbp3UniquePlayCount", this.PlaysLBP3Unique) +
|
LbpSerializer.StringElement("lbp3UniquePlayCount", this.PlaysLBP3Unique) +
|
||||||
LbpSerializer.StringElement("vitaCrossControlRequired", CrossControllerRequired) +
|
LbpSerializer.StringElement("vitaCrossControlRequired", this.CrossControllerRequired) +
|
||||||
LbpSerializer.StringElement("thumbsup", this.Thumbsup) +
|
LbpSerializer.StringElement("thumbsup", this.Thumbsup) +
|
||||||
LbpSerializer.StringElement("thumbsdown", this.Thumbsdown) +
|
LbpSerializer.StringElement("thumbsdown", this.Thumbsdown) +
|
||||||
LbpSerializer.StringElement("averageRating", this.RatingLBP1) +
|
LbpSerializer.StringElement("averageRating", this.RatingLBP1) +
|
||||||
|
@ -290,8 +306,8 @@ public class Slot
|
||||||
LbpSerializer.StringElement
|
LbpSerializer.StringElement
|
||||||
("yourLBPVitaPlayCount", yourVisitedStats?.PlaysLBPVita) + // i doubt this is the right name but we'll go with it
|
("yourLBPVitaPlayCount", yourVisitedStats?.PlaysLBPVita) + // i doubt this is the right name but we'll go with it
|
||||||
yourReview?.Serialize("yourReview") +
|
yourReview?.Serialize("yourReview") +
|
||||||
LbpSerializer.StringElement("reviewsEnabled", true) +
|
LbpSerializer.StringElement("reviewsEnabled", ServerSettings.Instance.LevelReviewsEnabled) +
|
||||||
LbpSerializer.StringElement("commentsEnabled", false) +
|
LbpSerializer.StringElement("commentsEnabled", ServerSettings.Instance.LevelCommentsEnabled) +
|
||||||
LbpSerializer.StringElement("reviewCount", this.ReviewCount);
|
LbpSerializer.StringElement("reviewCount", this.ReviewCount);
|
||||||
|
|
||||||
return LbpSerializer.TaggedStringElement("slot", slotData, "type", "user");
|
return LbpSerializer.TaggedStringElement("slot", slotData, "type", "user");
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Linq;
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
using LBPUnion.ProjectLighthouse.Serialization;
|
using LBPUnion.ProjectLighthouse.Serialization;
|
||||||
|
|
||||||
|
@ -15,27 +16,60 @@ public class Comment
|
||||||
|
|
||||||
public int PosterUserId { get; set; }
|
public int PosterUserId { get; set; }
|
||||||
|
|
||||||
public int TargetUserId { get; set; }
|
public int TargetId { get; set; }
|
||||||
|
|
||||||
[ForeignKey(nameof(PosterUserId))]
|
[ForeignKey(nameof(PosterUserId))]
|
||||||
public User Poster { get; set; }
|
public User Poster { get; set; }
|
||||||
|
|
||||||
[ForeignKey(nameof(TargetUserId))]
|
public bool Deleted { get; set; }
|
||||||
public User Target { get; set; }
|
|
||||||
|
public string DeletedType { get; set; }
|
||||||
|
|
||||||
|
public string DeletedBy { get; set; }
|
||||||
|
|
||||||
public long Timestamp { get; set; }
|
public long Timestamp { get; set; }
|
||||||
|
|
||||||
[XmlElement("message")]
|
[XmlElement("message")]
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public CommentType Type { get; set; }
|
||||||
|
|
||||||
public int ThumbsUp { get; set; }
|
public int ThumbsUp { get; set; }
|
||||||
public int ThumbsDown { get; set; }
|
public int ThumbsDown { get; set; }
|
||||||
|
|
||||||
|
public string getComment()
|
||||||
|
{
|
||||||
|
if (!this.Deleted)
|
||||||
|
{
|
||||||
|
return this.Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.DeletedBy == this.Poster.Username)
|
||||||
|
{
|
||||||
|
return "This comment has been deleted by the author.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using Database database = new();
|
||||||
|
User deletedBy = database.Users.FirstOrDefault(u => u.Username == this.DeletedBy);
|
||||||
|
|
||||||
|
if (deletedBy != null && deletedBy.UserId == this.TargetId)
|
||||||
|
{
|
||||||
|
return "This comment has been deleted by the player.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "This comment has been deleted.";
|
||||||
|
}
|
||||||
|
|
||||||
private string serialize()
|
private string serialize()
|
||||||
=> LbpSerializer.StringElement("id", this.CommentId) +
|
=> LbpSerializer.StringElement("id", this.CommentId) +
|
||||||
LbpSerializer.StringElement("npHandle", this.Poster.Username) +
|
LbpSerializer.StringElement("npHandle", this.Poster.Username) +
|
||||||
LbpSerializer.StringElement("timestamp", this.Timestamp) +
|
LbpSerializer.StringElement("timestamp", this.Timestamp) +
|
||||||
LbpSerializer.StringElement("message", this.Message) +
|
LbpSerializer.StringElement("message", this.Message) +
|
||||||
|
(this.Deleted ? LbpSerializer.StringElement("deleted", true) +
|
||||||
|
LbpSerializer.StringElement("deletedBy", this.DeletedBy) +
|
||||||
|
LbpSerializer.StringElement("deletedType", this.DeletedBy) : "") +
|
||||||
LbpSerializer.StringElement("thumbsup", this.ThumbsUp) +
|
LbpSerializer.StringElement("thumbsup", this.ThumbsUp) +
|
||||||
LbpSerializer.StringElement("thumbsdown", this.ThumbsDown);
|
LbpSerializer.StringElement("thumbsdown", this.ThumbsDown);
|
||||||
|
|
||||||
|
|
16
ProjectLighthouse/Types/Reaction.cs
Normal file
16
ProjectLighthouse/Types/Reaction.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace LBPUnion.ProjectLighthouse.Types;
|
||||||
|
|
||||||
|
public class Reaction
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int RatingId { get; set; }
|
||||||
|
|
||||||
|
public int UserId { get; set; }
|
||||||
|
|
||||||
|
public int TargetId { get; set; }
|
||||||
|
|
||||||
|
public int Rating { get; set; }
|
||||||
|
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ServerSettings
|
public class ServerSettings
|
||||||
{
|
{
|
||||||
public const int CurrentConfigVersion = 18; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE!
|
public const int CurrentConfigVersion = 19; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE!
|
||||||
private static FileSystemWatcher fileWatcher;
|
private static FileSystemWatcher fileWatcher;
|
||||||
static ServerSettings()
|
static ServerSettings()
|
||||||
{
|
{
|
||||||
|
@ -132,6 +132,12 @@ public class ServerSettings
|
||||||
|
|
||||||
public int PhotosQuota { get; set; } = 500;
|
public int PhotosQuota { get; set; } = 500;
|
||||||
|
|
||||||
|
public bool ProfileCommentsEnabled { get; set; } = true;
|
||||||
|
|
||||||
|
public bool LevelCommentsEnabled { get; set; } = true;
|
||||||
|
|
||||||
|
public bool LevelReviewsEnabled { get; set; } = true;
|
||||||
|
|
||||||
public bool GoogleAnalyticsEnabled { get; set; }
|
public bool GoogleAnalyticsEnabled { get; set; }
|
||||||
|
|
||||||
public string GoogleAnalyticsId { get; set; } = "";
|
public string GoogleAnalyticsId { get; set; } = "";
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class User
|
||||||
public int Comments {
|
public int Comments {
|
||||||
get {
|
get {
|
||||||
using Database database = new();
|
using Database database = new();
|
||||||
return database.Comments.Count(c => c.TargetUserId == this.UserId);
|
return database.Comments.Count(c => c.Type == CommentType.Profile && c.TargetId == this.UserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ public class User
|
||||||
LbpSerializer.StringElement("commentCount", this.Comments) +
|
LbpSerializer.StringElement("commentCount", this.Comments) +
|
||||||
LbpSerializer.StringElement("photosByMeCount", this.PhotosByMe) +
|
LbpSerializer.StringElement("photosByMeCount", this.PhotosByMe) +
|
||||||
LbpSerializer.StringElement("photosWithMeCount", this.PhotosWithMe) +
|
LbpSerializer.StringElement("photosWithMeCount", this.PhotosWithMe) +
|
||||||
LbpSerializer.StringElement("commentsEnabled", "true") +
|
LbpSerializer.StringElement("commentsEnabled", ServerSettings.Instance.ProfileCommentsEnabled) +
|
||||||
LbpSerializer.StringElement("location", this.Location.Serialize()) +
|
LbpSerializer.StringElement("location", this.Location.Serialize()) +
|
||||||
LbpSerializer.StringElement("favouriteSlotCount", this.HeartedLevels) +
|
LbpSerializer.StringElement("favouriteSlotCount", this.HeartedLevels) +
|
||||||
LbpSerializer.StringElement("favouriteUserCount", this.HeartedUsers) +
|
LbpSerializer.StringElement("favouriteUserCount", this.HeartedUsers) +
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue