mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-06-18 05:31:26 +00:00
Add proper ban page when logging in (#773)
* Add proper ban page upon logging in * Remove two extra line break tags that don't need to be there * Fix timestamp formatting * Properly display timestamps in correct timezone * Fix formatting issues with ban page * Remove extra parenthesis which would be rendered on-page * Add to redirect middleware to prevent navigating to other pages * Small nitpick, renaming UserBannedPage to BannedUserPage * Resolve nitpicks from reviewers * Remove un-necessary log message in LoginForm * Fix ban reason translatable string argument * Word choice nitpick ("Ban Created" -> "Ban Issued") * Final adjustments and nitpicks, visual and grammatical * Resolve requested changes from reviewers
This commit is contained in:
parent
c8c54d78de
commit
21dbdff20a
11 changed files with 203 additions and 23 deletions
51
ProjectLighthouse.Localization/Moderation.resx
Normal file
51
ProjectLighthouse.Localization/Moderation.resx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<root>
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>1.3</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="suspension_heading" xml:space="preserve">
|
||||||
|
<value>Account Suspended</value>
|
||||||
|
</data>
|
||||||
|
<data name="suspension_explanation" xml:space="preserve">
|
||||||
|
<value>Your {0} account has been suspended due to code of conduct violations.</value>
|
||||||
|
</data>
|
||||||
|
<data name="suspension_expiration" xml:space="preserve">
|
||||||
|
<value>During this suspension, the following features will be limited until {0}:</value>
|
||||||
|
</data>
|
||||||
|
<data name="suspension_reason" xml:space="preserve">
|
||||||
|
<value>Reason for suspension: "{0}"</value>
|
||||||
|
</data>
|
||||||
|
<data name="suspension_circumvent_warning" xml:space="preserve">
|
||||||
|
<value>Attempts to circumvent this suspension will result in an extended suspension period.</value>
|
||||||
|
</data>
|
||||||
|
<data name="lbp_online_multiplayer" xml:space="preserve">
|
||||||
|
<value>LittleBigPlanet™ Online multiplayer</value>
|
||||||
|
</data>
|
||||||
|
<data name="does_not_expire" xml:space="preserve">
|
||||||
|
<value>Does not expire</value>
|
||||||
|
</data>
|
||||||
|
<data name="profile_visibility" xml:space="preserve">
|
||||||
|
<value>Profile visibility</value>
|
||||||
|
</data>
|
||||||
|
<data name="website_interactions" xml:space="preserve">
|
||||||
|
<value>Browsing levels, photos, and profiles</value>
|
||||||
|
</data>
|
||||||
|
<data name="account_profile_management" xml:space="preserve">
|
||||||
|
<value>Account and profile management</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
|
@ -44,6 +44,10 @@
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
<LastGenOutput>TwoFactor.Designer.cs</LastGenOutput>
|
<LastGenOutput>TwoFactor.Designer.cs</LastGenOutput>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Update="Moderation.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Moderation.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -12,4 +12,5 @@ public enum TranslationAreas
|
||||||
Profile,
|
Profile,
|
||||||
ModPanel,
|
ModPanel,
|
||||||
TwoFactor,
|
TwoFactor,
|
||||||
|
Moderation,
|
||||||
}
|
}
|
|
@ -59,6 +59,18 @@ public class UserRequiredRedirectMiddleware : MiddlewareDBContext
|
||||||
return;
|
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 (user.EmailAddress == null && ServerConfiguration.Instance.Mail.MailEnabled)
|
||||||
{
|
{
|
||||||
if (!pathContains(ctx, "/login/setEmail"))
|
if (!pathContains(ctx, "/login/setEmail"))
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
@{
|
@{
|
||||||
Layout = "Layouts/BaseLayout";
|
Layout = "Layouts/BaseLayout";
|
||||||
Model.ShowTitleInPage = false;
|
Model.ShowTitleInPage = false;
|
||||||
|
|
||||||
bool isMobile = Request.IsMobile();
|
bool isMobile = Request.IsMobile();
|
||||||
string language = Model.GetLanguage();
|
string language = Model.GetLanguage();
|
||||||
string timeZone = Model.GetTimeZone();
|
string timeZone = Model.GetTimeZone();
|
||||||
|
@ -25,7 +25,9 @@
|
||||||
if (Model.PendingAuthAttempts > 0)
|
if (Model.PendingAuthAttempts > 0)
|
||||||
{
|
{
|
||||||
<p>
|
<p>
|
||||||
<b><a href="/authentication">@Model.Translate(LandingPageStrings.AuthAttemptsPending, Model.PendingAuthAttempts)</a></b>
|
<b>
|
||||||
|
<a href="/authentication">@Model.Translate(LandingPageStrings.AuthAttemptsPending, Model.PendingAuthAttempts)</a>
|
||||||
|
</b>
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,13 +50,13 @@
|
||||||
int i = 0;
|
int i = 0;
|
||||||
foreach (UserEntity user in Model.PlayersOnline)
|
foreach (UserEntity user in Model.PlayersOnline)
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
@await user.ToLink(Html, ViewData, language, timeZone, true)
|
@await user.ToLink(Html, ViewData, language, timeZone, true)
|
||||||
@* whitespace has forced my hand *@
|
@* whitespace has forced my hand *@
|
||||||
if (i != Model.PlayersOnline.Count)
|
if (i != Model.PlayersOnline.Count)
|
||||||
{
|
{
|
||||||
<span>,</span>
|
<span>,</span>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +65,9 @@
|
||||||
<div class="@(isMobile ? "" : "ui center aligned grid")">
|
<div class="@(isMobile ? "" : "ui center aligned grid")">
|
||||||
<div class="eight wide column">
|
<div class="eight wide column">
|
||||||
<div class="ui inverted pink segment">
|
<div class="ui inverted pink segment">
|
||||||
<h1><i class="star icon"></i>@Model.Translate(LandingPageStrings.LatestTeamPicks)</h1>
|
<h1>
|
||||||
|
<i class="star icon"></i>@Model.Translate(LandingPageStrings.LatestTeamPicks)
|
||||||
|
</h1>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="ui left aligned segment">
|
<div class="ui left aligned segment">
|
||||||
@foreach (SlotEntity slot in Model.LatestTeamPicks!) @* Can't reach a point where this is null *@
|
@foreach (SlotEntity slot in Model.LatestTeamPicks!) @* Can't reach a point where this is null *@
|
||||||
|
@ -80,7 +84,9 @@
|
||||||
}
|
}
|
||||||
<div class="eight wide column">
|
<div class="eight wide column">
|
||||||
<div class="ui inverted blue segment">
|
<div class="ui inverted blue segment">
|
||||||
<h1><i class="globe americas icon"></i>@Model.Translate(LandingPageStrings.NewestLevels)</h1>
|
<h1>
|
||||||
|
<i class="globe americas icon"></i>@Model.Translate(LandingPageStrings.NewestLevels)
|
||||||
|
</h1>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="ui left aligned segment">
|
<div class="ui left aligned segment">
|
||||||
@foreach (SlotEntity slot in Model.NewestLevels!) @* Can't reach a point where this is null *@
|
@foreach (SlotEntity slot in Model.NewestLevels!) @* Can't reach a point where this is null *@
|
||||||
|
|
|
@ -13,14 +13,14 @@ namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages;
|
||||||
|
|
||||||
public class LandingPage : BaseLayout
|
public class LandingPage : BaseLayout
|
||||||
{
|
{
|
||||||
public LandingPage(DatabaseContext database) : base(database)
|
public List<SlotEntity>? LatestTeamPicks;
|
||||||
{}
|
public List<SlotEntity>? NewestLevels;
|
||||||
|
|
||||||
public int PendingAuthAttempts;
|
public int PendingAuthAttempts;
|
||||||
public List<UserEntity> PlayersOnline = new();
|
public List<UserEntity> PlayersOnline = new();
|
||||||
|
|
||||||
public List<SlotEntity>? LatestTeamPicks;
|
public LandingPage(DatabaseContext database) : base(database)
|
||||||
public List<SlotEntity>? NewestLevels;
|
{ }
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public async Task<IActionResult> OnGet()
|
public async Task<IActionResult> OnGet()
|
||||||
|
@ -29,10 +29,12 @@ public class LandingPage : BaseLayout
|
||||||
if (user != null && user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired");
|
if (user != null && user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired");
|
||||||
|
|
||||||
if (user != null)
|
if (user != null)
|
||||||
this.PendingAuthAttempts = await this.Database.PlatformLinkAttempts
|
this.PendingAuthAttempts =
|
||||||
.CountAsync(l => l.UserId == user.UserId);
|
await this.Database.PlatformLinkAttempts.CountAsync(l => l.UserId == user.UserId);
|
||||||
|
|
||||||
List<int> userIds = await this.Database.LastContacts.Where(l => TimeHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync();
|
List<int> 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();
|
this.PlayersOnline = await this.Database.Users.Where(u => userIds.Contains(u.UserId)).ToListAsync();
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="ui middle aligned left aligned">
|
||||||
|
<h1>@Model.Translate(ModerationStrings.SuspensionHeading)</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
@Model.Translate(ModerationStrings.SuspensionExplanation, ServerConfiguration.Instance.Customization.ServerName)
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<span>
|
||||||
|
@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))
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>@Model.Translate(ModerationStrings.LbpOnlineMultiplayer)</li>
|
||||||
|
<li>@Model.Translate(ModerationStrings.WebsiteInteractions)</li>
|
||||||
|
<li>@Model.Translate(ModerationStrings.ProfileVisibility)</li>
|
||||||
|
<li>@Model.Translate(ModerationStrings.AccountProfileManagement)</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
@Model.Translate(ModerationStrings.SuspensionReason, Model.ModCase.Reason)
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
@Model.Translate(ModerationStrings.SuspensionCircumventWarning)
|
||||||
|
</p>
|
||||||
|
</div>
|
|
@ -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<IActionResult> 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -83,13 +83,6 @@ public class LoginForm : BaseLayout
|
||||||
return this.Page();
|
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()
|
WebTokenEntity webToken = new()
|
||||||
{
|
{
|
||||||
UserId = user.UserId,
|
UserId = user.UserId,
|
||||||
|
@ -120,6 +113,11 @@ public class LoginForm : BaseLayout
|
||||||
: this.Redirect("~/2fa" + "?redirect=" + HttpUtility.UrlEncode(redirect));
|
: this.Redirect("~/2fa" + "?redirect=" + HttpUtility.UrlEncode(redirect));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.IsBanned)
|
||||||
|
{
|
||||||
|
return this.Redirect("~/banned");
|
||||||
|
}
|
||||||
|
|
||||||
if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired");
|
if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired");
|
||||||
|
|
||||||
return ServerConfiguration.Instance.Mail.MailEnabled switch
|
return ServerConfiguration.Instance.Mail.MailEnabled switch
|
||||||
|
|
|
@ -23,18 +23,18 @@
|
||||||
@if (Model.ProfileUser.IsBanned)
|
@if (Model.ProfileUser.IsBanned)
|
||||||
{
|
{
|
||||||
<div class="ui inverted red segment">
|
<div class="ui inverted red segment">
|
||||||
<h3 style="margin-bottom:3px;"><i class="ban icon"></i> This user is currently banned.</h3>
|
<h3 style="margin-bottom:5px;"><i class="ban icon"></i> This user is currently banned.</h3>
|
||||||
@if (Model.User != null && Model.User.IsModerator)
|
@if (Model.User != null && Model.User.IsModerator)
|
||||||
{
|
{
|
||||||
<b>Reason:</b>
|
<b>Reason:</b>
|
||||||
<span>"@Model.ProfileUser.BannedReason"</span>
|
<span>"@Model.ProfileUser.BannedReason"</span> <br />
|
||||||
<p>
|
<p>
|
||||||
<i>Only you and other moderators may view the ban reason.</i>
|
<i>Only you and other moderators may view the ban reason.</i>
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<p>Users who engage in inappropriate, offensive, or violent actions will be moderated. Be sure to follow the rules!</p>
|
<p>This user has been banned for violating the Terms of Service. Remember to follow the rules!</p>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue