diff --git a/ProjectLighthouse.Localization/Moderation.resx b/ProjectLighthouse.Localization/Moderation.resx
new file mode 100644
index 00000000..b6366ba7
--- /dev/null
+++ b/ProjectLighthouse.Localization/Moderation.resx
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Account Suspended
+
+
+ Your {0} account has been suspended due to code of conduct violations.
+
+
+ During this suspension, the following features will be limited until {0}:
+
+
+ Reason for suspension: "{0}"
+
+
+ Attempts to circumvent this suspension will result in an extended suspension period.
+
+
+ LittleBigPlanet™ Online multiplayer
+
+
+ Does not expire
+
+
+ Profile visibility
+
+
+ Browsing levels, photos, and profiles
+
+
+ Account and profile management
+
+
\ No newline at end of file
diff --git a/ProjectLighthouse.Localization/ProjectLighthouse.Localization.csproj b/ProjectLighthouse.Localization/ProjectLighthouse.Localization.csproj
index cd236539..69348286 100644
--- a/ProjectLighthouse.Localization/ProjectLighthouse.Localization.csproj
+++ b/ProjectLighthouse.Localization/ProjectLighthouse.Localization.csproj
@@ -44,6 +44,10 @@
ResXFileCodeGenerator
TwoFactor.Designer.cs
+
+ ResXFileCodeGenerator
+ Moderation.Designer.cs
+
diff --git a/ProjectLighthouse.Localization/StringLists/ModerationStrings.cs b/ProjectLighthouse.Localization/StringLists/ModerationStrings.cs
new file mode 100644
index 00000000..5a6b1ebe
--- /dev/null
+++ b/ProjectLighthouse.Localization/StringLists/ModerationStrings.cs
@@ -0,0 +1,22 @@
+namespace LBPUnion.ProjectLighthouse.Localization.StringLists;
+
+public static class ModerationStrings
+{
+ // Main page strings
+ public static readonly TranslatableString SuspensionHeading = create("suspension_heading");
+ public static readonly TranslatableString SuspensionExplanation = create("suspension_explanation");
+ public static readonly TranslatableString SuspensionExpiration = create("suspension_expiration");
+ public static readonly TranslatableString SuspensionReason = create("suspension_reason");
+ public static readonly TranslatableString SuspensionCircumventWarning = create("suspension_circumvent_warning");
+
+ // Translatable string in case a ban doesn't expire
+ public static readonly TranslatableString DoesNotExpire = create("does_not_expire");
+
+ // Restricted features strings
+ public static readonly TranslatableString LbpOnlineMultiplayer = create("lbp_online_multiplayer");
+ public static readonly TranslatableString WebsiteInteractions = create("website_interactions");
+ public static readonly TranslatableString ProfileVisibility = create("profile_visibility");
+ public static readonly TranslatableString AccountProfileManagement = create("account_profile_management");
+
+ private static TranslatableString create(string key) => new(TranslationAreas.Moderation, key);
+}
\ No newline at end of file
diff --git a/ProjectLighthouse.Localization/TranslationAreas.cs b/ProjectLighthouse.Localization/TranslationAreas.cs
index b1d1ce68..106be35f 100644
--- a/ProjectLighthouse.Localization/TranslationAreas.cs
+++ b/ProjectLighthouse.Localization/TranslationAreas.cs
@@ -12,4 +12,5 @@ public enum TranslationAreas
Profile,
ModPanel,
TwoFactor,
+ Moderation,
}
\ No newline at end of file
diff --git a/ProjectLighthouse.Servers.Website/Middlewares/UserRequiredRedirectMiddleware.cs b/ProjectLighthouse.Servers.Website/Middlewares/UserRequiredRedirectMiddleware.cs
index a6e8a68a..32bb0586 100644
--- a/ProjectLighthouse.Servers.Website/Middlewares/UserRequiredRedirectMiddleware.cs
+++ b/ProjectLighthouse.Servers.Website/Middlewares/UserRequiredRedirectMiddleware.cs
@@ -59,6 +59,18 @@ public class UserRequiredRedirectMiddleware : MiddlewareDBContext
return;
}
+ if (user.IsBanned)
+ {
+ if (!pathContains(ctx, "/banned"))
+ {
+ ctx.Response.Redirect("/banned");
+ return;
+ }
+
+ await this.next(ctx);
+ return;
+ }
+
if (user.EmailAddress == null && ServerConfiguration.Instance.Mail.MailEnabled)
{
if (!pathContains(ctx, "/login/setEmail"))
diff --git a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml
index 5749f0ce..903dbf0c 100644
--- a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml
+++ b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml
@@ -10,7 +10,7 @@
@{
Layout = "Layouts/BaseLayout";
Model.ShowTitleInPage = false;
-
+
bool isMobile = Request.IsMobile();
string language = Model.GetLanguage();
string timeZone = Model.GetTimeZone();
@@ -25,7 +25,9 @@
if (Model.PendingAuthAttempts > 0)
{
- @Model.Translate(LandingPageStrings.AuthAttemptsPending, Model.PendingAuthAttempts)
+
+ @Model.Translate(LandingPageStrings.AuthAttemptsPending, Model.PendingAuthAttempts)
+
}
}
@@ -48,13 +50,13 @@
int i = 0;
foreach (UserEntity user in Model.PlayersOnline)
{
- i++;
+ i++;
@await user.ToLink(Html, ViewData, language, timeZone, true)
@* whitespace has forced my hand *@
if (i != Model.PlayersOnline.Count)
{
,
- }
+ }
}
}
@@ -63,7 +65,9 @@
-
@Model.Translate(LandingPageStrings.LatestTeamPicks)
+
+ @Model.Translate(LandingPageStrings.LatestTeamPicks)
+
@foreach (SlotEntity slot in Model.LatestTeamPicks!) @* Can't reach a point where this is null *@
@@ -80,7 +84,9 @@
}
-
@Model.Translate(LandingPageStrings.NewestLevels)
+
+ @Model.Translate(LandingPageStrings.NewestLevels)
+
@foreach (SlotEntity slot in Model.NewestLevels!) @* Can't reach a point where this is null *@
diff --git a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs
index d88ac9d8..7dd9a1ba 100644
--- a/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs
+++ b/ProjectLighthouse.Servers.Website/Pages/LandingPage.cshtml.cs
@@ -13,14 +13,14 @@ namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages;
public class LandingPage : BaseLayout
{
- public LandingPage(DatabaseContext database) : base(database)
- {}
+ public List
? LatestTeamPicks;
+ public List? NewestLevels;
public int PendingAuthAttempts;
public List PlayersOnline = new();
- public List? LatestTeamPicks;
- public List? NewestLevels;
+ public LandingPage(DatabaseContext database) : base(database)
+ { }
[UsedImplicitly]
public async Task OnGet()
@@ -29,10 +29,12 @@ public class LandingPage : BaseLayout
if (user != null && user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired");
if (user != null)
- this.PendingAuthAttempts = await this.Database.PlatformLinkAttempts
- .CountAsync(l => l.UserId == user.UserId);
+ this.PendingAuthAttempts =
+ await this.Database.PlatformLinkAttempts.CountAsync(l => l.UserId == user.UserId);
- List userIds = await this.Database.LastContacts.Where(l => TimeHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync();
+ List userIds = await this.Database.LastContacts.Where(l => TimeHelper.Timestamp - l.Timestamp < 300)
+ .Select(l => l.UserId)
+ .ToListAsync();
this.PlayersOnline = await this.Database.Users.Where(u => userIds.Contains(u.UserId)).ToListAsync();
diff --git a/ProjectLighthouse.Servers.Website/Pages/Login/BannedUserPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/Login/BannedUserPage.cshtml
new file mode 100644
index 00000000..4924e5f1
--- /dev/null
+++ b/ProjectLighthouse.Servers.Website/Pages/Login/BannedUserPage.cshtml
@@ -0,0 +1,48 @@
+@page "/banned"
+@using LBPUnion.ProjectLighthouse.Configuration
+@using LBPUnion.ProjectLighthouse.Localization.StringLists
+@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.Login.BannedUserPage
+
+@{
+ Layout = "Layouts/BaseLayout";
+ Model.Title = Model.Translate(ModerationStrings.SuspensionHeading);
+
+ string timeZone = (string?)ViewData["TimeZone"] ?? TimeZoneInfo.Local.Id;
+ TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
+}
+
+
+
@Model.Translate(ModerationStrings.SuspensionHeading)
+
+
+ @Model.Translate(ModerationStrings.SuspensionExplanation, ServerConfiguration.Instance.Customization.ServerName)
+
+
+
+
+ @if (Model.ModCase.ExpiresAt != null)
+ {
+ @Model.Translate(ModerationStrings.SuspensionExpiration, TimeZoneInfo.ConvertTime(Model.ModCase.ExpiresAt.Value, timeZoneInfo).ToString("M/d/yyyy @ h:mm tt"))
+ }
+ else
+ {
+ @Model.Translate(ModerationStrings.SuspensionExpiration, Model.Translate(ModerationStrings.DoesNotExpire))
+ }
+
+
+
+ - @Model.Translate(ModerationStrings.LbpOnlineMultiplayer)
+ - @Model.Translate(ModerationStrings.WebsiteInteractions)
+ - @Model.Translate(ModerationStrings.ProfileVisibility)
+ - @Model.Translate(ModerationStrings.AccountProfileManagement)
+
+
+
+
+ @Model.Translate(ModerationStrings.SuspensionReason, Model.ModCase.Reason)
+
+
+
+ @Model.Translate(ModerationStrings.SuspensionCircumventWarning)
+
+
\ No newline at end of file
diff --git a/ProjectLighthouse.Servers.Website/Pages/Login/BannedUserPage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Login/BannedUserPage.cshtml.cs
new file mode 100644
index 00000000..b7cc2ea7
--- /dev/null
+++ b/ProjectLighthouse.Servers.Website/Pages/Login/BannedUserPage.cshtml.cs
@@ -0,0 +1,36 @@
+using JetBrains.Annotations;
+using LBPUnion.ProjectLighthouse.Database;
+using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
+using LBPUnion.ProjectLighthouse.Types.Entities.Moderation;
+using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
+using LBPUnion.ProjectLighthouse.Types.Moderation.Cases;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.Login;
+
+public class BannedUserPage : BaseLayout
+{
+ public BannedUserPage(DatabaseContext database) : base(database)
+ { }
+
+ public ModerationCaseEntity ModCase = null!;
+
+ [UsedImplicitly]
+ public async Task OnGet()
+ {
+ UserEntity? user = this.Database.UserFromWebRequest(this.Request);
+
+ if (user == null) return this.Redirect("~/login");
+ if (!user.IsBanned) return this.Redirect("~/");
+
+ ModerationCaseEntity? modCase = await this.Database.Cases
+ .FirstOrDefaultAsync(c => c.AffectedId == user.UserId && c.Type == CaseType.UserBan);
+
+ if (modCase == null) return this.Redirect("~/");
+
+ this.ModCase = modCase;
+
+ return this.Page();
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse.Servers.Website/Pages/Login/LoginForm.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Login/LoginForm.cshtml.cs
index 47f919e1..27346ce9 100644
--- a/ProjectLighthouse.Servers.Website/Pages/Login/LoginForm.cshtml.cs
+++ b/ProjectLighthouse.Servers.Website/Pages/Login/LoginForm.cshtml.cs
@@ -83,13 +83,6 @@ public class LoginForm : BaseLayout
return this.Page();
}
- if (user.IsBanned)
- {
- Logger.Warn($"User {user.Username} (id: {user.UserId}) failed to login on web due to being banned", LogArea.Login);
- this.Error = this.Translate(ErrorStrings.UserIsBanned, user.BannedReason);
- return this.Page();
- }
-
WebTokenEntity webToken = new()
{
UserId = user.UserId,
@@ -120,6 +113,11 @@ public class LoginForm : BaseLayout
: this.Redirect("~/2fa" + "?redirect=" + HttpUtility.UrlEncode(redirect));
}
+ if (user.IsBanned)
+ {
+ return this.Redirect("~/banned");
+ }
+
if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired");
return ServerConfiguration.Instance.Mail.MailEnabled switch
diff --git a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml
index eb42c093..99d5588d 100644
--- a/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml
+++ b/ProjectLighthouse.Servers.Website/Pages/UserPage.cshtml
@@ -23,18 +23,18 @@
@if (Model.ProfileUser.IsBanned)
{
-
This user is currently banned.
+
This user is currently banned.
@if (Model.User != null && Model.User.IsModerator)
{
Reason:
-
"@Model.ProfileUser.BannedReason"
+
"@Model.ProfileUser.BannedReason"
Only you and other moderators may view the ban reason.
}
else
{
-
Users who engage in inappropriate, offensive, or violent actions will be moderated. Be sure to follow the rules!
+
This user has been banned for violating the Terms of Service. Remember to follow the rules!
}
}