Skip to content
Merged
6 changes: 6 additions & 0 deletions ProjectLighthouse.Localization/BaseLayout.resx
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,10 @@
<data name="license_warn_3" xml:space="preserve">
<value>If not, please publish the source code somewhere accessible to your users.</value>
</data>
<data name="read_only_warn_title" xml:space="preserve">
<value>Read-Only Mode</value>
</data>
<data name="read_only_warn" xml:space="preserve">
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ public static class BaseLayoutStrings
public static readonly TranslatableString LicenseWarn2 = create("license_warn_2");
public static readonly TranslatableString LicenseWarn3 = create("license_warn_3");

public static readonly TranslatableString ReadOnlyWarnTitle = create("read_only_warn_title");
public static readonly TranslatableString ReadOnlyWarn = create("read_only_warn");

private static TranslatableString create(string key) => new(TranslationAreas.BaseLayout, key);
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ public async Task<IActionResult> PostComment(string? username, string? slotType,
{
GameTokenEntity token = this.GetToken();

// Deny request if in read-only mode
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();

GameComment? comment = await this.DeserializeBody<GameComment>();
if (comment?.Message == null) return this.BadRequest();

Expand Down Expand Up @@ -159,6 +162,9 @@ public async Task<IActionResult> DeleteComment([FromQuery] int commentId, string
{
GameTokenEntity token = this.GetToken();

// Deny request if in read-only mode
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();

if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest();

CommentEntity? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Localization;
using LBPUnion.ProjectLighthouse.Localization.StringLists;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types.Entities.Notifications;
Expand Down Expand Up @@ -59,6 +61,11 @@ public async Task<IActionResult> Announce()
announceText.Replace("%user", username);
announceText.Replace("%id", token.UserId.ToString());

if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode)
{
announceText.Insert(0, BaseLayoutStrings.ReadOnlyWarn.Translate(LocalizationManager.DefaultLang));
}

#if DEBUG
announceText.Append("\n\n---DEBUG INFO---\n" +
$"user.UserId: {token.UserId}\n" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public async Task<IActionResult> UploadPhoto()
{
GameTokenEntity token = this.GetToken();

// Deny request if in read-only mode
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();

int photoCount = await this.database.Photos.CountAsync(p => p.CreatorId == token.UserId);
if (photoCount >= ServerConfiguration.Instance.UserGeneratedContentLimits.PhotosQuota) return this.BadRequest();

Expand Down Expand Up @@ -90,7 +93,7 @@ public async Task<IActionResult> UploadPhoto()
case SlotType.Developer:
{
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == photoSlot.SlotType && s.InternalSlotId == photoSlot.SlotId);
if (slot != null)
if (slot != null)
photoSlot.SlotId = slot.SlotId;
else
photoSlot.SlotId = await SlotHelper.GetPlaceholderSlotId(this.database, photoSlot.SlotId, photoSlot.SlotType);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#nullable enable
using System.Text;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Files;
using LBPUnion.ProjectLighthouse.Logging;
Expand Down Expand Up @@ -58,10 +59,14 @@ public async Task<IActionResult> UploadResource(string hash)
string fullPath = Path.GetFullPath(path);

FileHelper.EnsureDirectoryCreated(assetsDirectory);
// lbp treats code 409 as success and as an indicator that the file is already present

// Deny request if in read-only mode
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();

// LBP treats code 409 as success and as an indicator that the file is already present
if (FileHelper.ResourceExists(hash)) return this.Conflict();

// theoretically shouldn't be possible because of hash check but handle anyways
// Theoretically shouldn't be possible because of hash check but handle anyways
if (!fullPath.StartsWith(FileHelper.FullResourcePath)) return this.BadRequest();

Logger.Info($"Processing resource upload (hash: {hash})", LogArea.Resources);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public async Task<IActionResult> StartPublish()
UserEntity? user = await this.database.UserFromGameToken(token);
if (user == null) return this.Forbid();

// Deny request if in read-only mode
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();

GameUserSlot? slot = await this.DeserializeBody<GameUserSlot>();
if (slot == null)
{
Expand Down Expand Up @@ -116,6 +119,9 @@ public async Task<IActionResult> Publish([FromQuery] string? game)
UserEntity? user = await this.database.UserFromGameToken(token);
if (user == null) return this.Forbid();

// Deny request if in read-only mode
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();

GameUserSlot? slot = await this.DeserializeBody<GameUserSlot>();

if (slot == null)
Expand Down Expand Up @@ -335,6 +341,9 @@ public async Task<IActionResult> Unpublish(int id)
{
GameTokenEntity token = this.GetToken();

// Deny request if in read-only mode
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();

SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
if (slot == null) return this.NotFound();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Helpers;
Expand Down Expand Up @@ -92,6 +93,9 @@ public async Task<IActionResult> PostReview(int slotId)
{
GameTokenEntity token = this.GetToken();

// Deny request if in read-only mode
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();

GameReview? newReview = await this.DeserializeBody<GameReview>();
if (newReview == null) return this.BadRequest();

Expand All @@ -115,7 +119,7 @@ public async Task<IActionResult> PostReview(int slotId)
}
review.Thumb = Math.Clamp(newReview.Thumb, -1, 1);
review.LabelCollection = LabelHelper.RemoveInvalidLabels(newReview.LabelCollection);

review.Text = newReview.Text;
review.Deleted = false;
review.Timestamp = TimeHelper.TimestampMillis;
Expand Down Expand Up @@ -239,6 +243,9 @@ public async Task<IActionResult> DeleteReview(int slotId, string username)
{
GameTokenEntity token = this.GetToken();

// Deny request if in read-only mode
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();

int creatorId = await this.database.Slots.Where(s => s.SlotId == slotId).Select(s => s.CreatorId).FirstOrDefaultAsync();
if (creatorId == 0) return this.BadRequest();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.Json;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
using LBPUnion.ProjectLighthouse.Files;
Expand Down Expand Up @@ -73,6 +74,9 @@ public async Task<IActionResult> UpdateUser()

if (update.Biography != null)
{
// Deny request if in read-only mode
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();

if (update.Biography.Length > 512) return this.BadRequest();

user.Biography = update.Biography;
Expand All @@ -85,6 +89,9 @@ public async Task<IActionResult> UpdateUser()
{
if (string.IsNullOrWhiteSpace(resource)) continue;

// Deny request if in read-only mode
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();

if (!FileHelper.ResourceExists(resource) && !resource.StartsWith('g')) return this.BadRequest();

if (!GameResourceHelper.IsValidTexture(resource)) return this.BadRequest();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

// I would like to apologize in advance for anyone dealing with this file.
// I would like to apologize in advance for anyone dealing with this file.
// Theres probably a better way to do this with delegates but I'm tired.
// TODO: Clean up this file
// - jvyden
Expand Down Expand Up @@ -63,6 +63,9 @@ public async Task<IActionResult> PostComment([FromRoute] int id, [FromForm] stri
WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request);
if (token == null) return this.Redirect("~/login");

// Deny request if in read-only mode
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.Redirect("~/slot/" + id);

if (msg == null)
{
Logger.Error($"Refusing to post comment from {token.UserId} on level {id}, {nameof(msg)} is null", LogArea.Comments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public async Task<IActionResult> PostComment([FromRoute] int id, [FromForm] stri
WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request);
if (token == null) return this.Redirect("~/login");

// Deny request if in read-only mode
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.Redirect("~/user/" + id);

if (msg == null)
{
Logger.Error($"Refusing to post comment from {token.UserId} on user {id}, {nameof(msg)} is null", LogArea.Comments);
Expand Down
37 changes: 36 additions & 1 deletion ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,42 @@
}
}

<br><br>
@if (Model.LatestAnnouncement != null)
{
<div class="ui blue segment" style="position: relative;">
<div>
<h3>@Model.LatestAnnouncement.Title</h3>
<div style="padding-bottom: 2em;">
@if (Model.LatestAnnouncement.Content.Length > 250)
{
<span style="white-space: pre-line">
@Model.LatestAnnouncement.Content[..250]...
<a href="@ServerConfiguration.Instance.ExternalUrl/notifications">read more</a>
</span>
}
else
{
<span style="white-space: pre-line">
@Model.LatestAnnouncement.Content
</span>
}
</div>
@if (Model.LatestAnnouncement.Publisher != null)
{
<div class="ui tiny bottom left attached label">
Posted by
<a style="color: black" href="~/user/@Model.LatestAnnouncement.Publisher.UserId">
@Model.LatestAnnouncement.Publisher.Username
</a>
</div>
}
</div>
</div>
}
else
{
<br /><br />
}

<div class="@(isMobile ? "" : "ui center aligned grid")">
<div class="eight wide column">
Expand Down
7 changes: 7 additions & 0 deletions ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using LBPUnion.ProjectLighthouse.Types.Entities.Website;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
Expand All @@ -19,6 +20,8 @@ public class LandingPage : BaseLayout
public int PendingAuthAttempts;
public List<UserEntity> PlayersOnline = new();

public WebsiteAnnouncementEntity? LatestAnnouncement;

public LandingPage(DatabaseContext database) : base(database)
{ }

Expand Down Expand Up @@ -54,6 +57,10 @@ public async Task<IActionResult> OnGet()
.Include(s => s.Creator)
.ToListAsync();

this.LatestAnnouncement = await this.Database.WebsiteAnnouncements.Include(a => a.Publisher)
.OrderByDescending(a => a.AnnouncementId)
.FirstOrDefaultAsync();

return this.Page();
}
}
12 changes: 12 additions & 0 deletions ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,18 @@
</div>
</div>
}
@if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode)
{
<div class="ui bottom attached red message large">
<div class="ui container">
<i class="warning icon"></i>
<span style="font-size: 1.2rem;">@Model.Translate(BaseLayoutStrings.ReadOnlyWarnTitle)</span>
<p>
@Html.Raw(Model.Translate(BaseLayoutStrings.ReadOnlyWarn))
</p>
</div>
</div>
}
</header>
<div class="main">
<div class="ui container">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@using System.Web
@using LBPUnion.ProjectLighthouse.Configuration
@using LBPUnion.ProjectLighthouse.Database
@using LBPUnion.ProjectLighthouse.Localization
@using LBPUnion.ProjectLighthouse.Servers.Website.Extensions
Expand Down Expand Up @@ -31,18 +32,32 @@
@if (Model.CommentsEnabled && Model.User != null)
{
<div class="ui divider"></div>
<form class="ui reply form" action="postComment" method="post">
<div class="field">
<textarea style="min-height: 70px; height: 70px; max-height:120px" maxlength="100" name="msg"></textarea>
@if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode)
{
<div class="ui red segment">
<p>
<i>
@ServerConfiguration.Instance.Customization.ServerName is currently in read-only mode.
You will not be able to post comments until read-only mode is disabled.
</i>
</p>
</div>
<input type="submit" class="ui blue button">
</form>
}
else
{
<form class="ui reply form" action="postComment" method="post">
<div class="field">
<textarea style="min-height: 70px; height: 70px; max-height:120px" maxlength="100" name="msg"></textarea>
</div>
<input type="submit" class="ui blue button">
</form>
}
@if (Model.Comments.Count > 0)
{
<div class="ui divider"></div>
}
}

@{
int i = 0;
foreach (KeyValuePair<CommentEntity, RatedCommentEntity?> commentAndReaction in Model.Comments)
Expand Down
Loading