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!

}
}