mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-07-23 21:51:29 +00:00
Implement URL slugs (#931)
* Implement URL slugs for Users and Slots * Fix extra spaces in slot slugs
This commit is contained in:
parent
aea66b4a74
commit
1eb3bfa2ec
8 changed files with 55 additions and 8 deletions
|
@ -0,0 +1,33 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Web;
|
||||||
|
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||||
|
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||||
|
|
||||||
|
namespace LBPUnion.ProjectLighthouse.Servers.Website.Extensions;
|
||||||
|
|
||||||
|
public static partial class SlugExtensions
|
||||||
|
{
|
||||||
|
[GeneratedRegex("[^a-zA-Z0-9 ]")]
|
||||||
|
private static partial Regex ValidSlugCharactersRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"[\s]{2,}")]
|
||||||
|
private static partial Regex WhitespaceRegex();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a URL slug that only contains alphanumeric characters
|
||||||
|
/// with spaces replaced with dashes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slot">The slot to generate the slug for</param>
|
||||||
|
/// <returns>A string containing the url slug for this slot</returns>
|
||||||
|
public static string GenerateSlug(this SlotEntity slot) =>
|
||||||
|
slot.Name.Length == 0
|
||||||
|
? "unnamed-level"
|
||||||
|
: WhitespaceRegex().Replace(ValidSlugCharactersRegex().Replace(HttpUtility.HtmlDecode(slot.Name), ""), " ").Replace(" ", "-").ToLower();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a URL slug for the given user
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The user to generate the slug for</param>
|
||||||
|
/// <returns>A string containing the url slug for this user</returns>
|
||||||
|
public static string GenerateSlug(this UserEntity user) => user.Username.ToLower();
|
||||||
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
string userStatus = includeStatus ? Model.GetStatus(Database).ToTranslatedString(language, timeZone) : "";
|
string userStatus = includeStatus ? Model.GetStatus(Database).ToTranslatedString(language, timeZone) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
<a href="/user/@Model.UserId" title="@userStatus" class="user-link">
|
<a href="/user/@Model.UserId/@Model.GenerateSlug()" title="@userStatus" class="user-link">
|
||||||
<img src="/gameAssets/@Model.WebsiteAvatarHash" alt=""/>
|
<img src="/gameAssets/@Model.WebsiteAvatarHash" alt=""/>
|
||||||
|
|
||||||
@if (Model.IsModerator)
|
@if (Model.IsModerator)
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
@if (showLink)
|
@if (showLink)
|
||||||
{
|
{
|
||||||
<h2>
|
<h2>
|
||||||
<a href="~/slot/@Model.SlotId">@slotName</a> <i class="@Model.GetLevelLockIcon()"></i>
|
<a href="~/slot/@Model.SlotId/@Model.GenerateSlug()">@slotName</a> <i class="@Model.GetLevelLockIcon()"></i>
|
||||||
</h2>
|
</h2>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -68,7 +68,7 @@
|
||||||
@if (showLink)
|
@if (showLink)
|
||||||
{
|
{
|
||||||
<h3>
|
<h3>
|
||||||
<a href="~/slot/@Model.SlotId">@slotName</a> <i class="@Model.GetLevelLockIcon()"></i>
|
<a href="~/slot/@Model.SlotId/@Model.GenerateSlug()">@slotName</a> <i class="@Model.GetLevelLockIcon()"></i>
|
||||||
</h3>
|
</h3>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
@if (showLink)
|
@if (showLink)
|
||||||
{
|
{
|
||||||
<h2 style="margin-bottom: 2px;">
|
<h2 style="margin-bottom: 2px;">
|
||||||
<a href="~/user/@Model.UserId">@Model.Username</a>
|
<a href="~/user/@Model.UserId/@Model.GenerateSlug()">@Model.Username</a>
|
||||||
@if (Model.IsModerator)
|
@if (Model.IsModerator)
|
||||||
{
|
{
|
||||||
<span class="profile-tag ui label @Model.PermissionLevel.ToHtmlColor()">
|
<span class="profile-tag ui label @Model.PermissionLevel.ToHtmlColor()">
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@page "/slot/{id:int}"
|
@page "/slot/{id:int}/{slug?}"
|
||||||
@using System.Web
|
@using System.Web
|
||||||
@using LBPUnion.ProjectLighthouse.Database
|
@using LBPUnion.ProjectLighthouse.Database
|
||||||
@using LBPUnion.ProjectLighthouse.Extensions
|
@using LBPUnion.ProjectLighthouse.Extensions
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#nullable enable
|
#nullable enable
|
||||||
using LBPUnion.ProjectLighthouse.Configuration;
|
using LBPUnion.ProjectLighthouse.Configuration;
|
||||||
using LBPUnion.ProjectLighthouse.Database;
|
using LBPUnion.ProjectLighthouse.Database;
|
||||||
|
using LBPUnion.ProjectLighthouse.Servers.Website.Extensions;
|
||||||
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
|
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
|
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||||
|
@ -28,7 +29,7 @@ public class SlotPage : BaseLayout
|
||||||
public SlotPage(DatabaseContext database) : base(database)
|
public SlotPage(DatabaseContext database) : base(database)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet([FromRoute] int id)
|
public async Task<IActionResult> OnGet([FromRoute] int id, string? slug)
|
||||||
{
|
{
|
||||||
SlotEntity? slot = await this.Database.Slots.Include(s => s.Creator)
|
SlotEntity? slot = await this.Database.Slots.Include(s => s.Creator)
|
||||||
.Where(s => s.Type == SlotType.User || (this.User != null && this.User.PermissionLevel >= PermissionLevel.Moderator))
|
.Where(s => s.Type == SlotType.User || (this.User != null && this.User.PermissionLevel >= PermissionLevel.Moderator))
|
||||||
|
@ -45,6 +46,12 @@ public class SlotPage : BaseLayout
|
||||||
if ((slot.Hidden || slot.SubLevel && (this.User == null && this.User != slot.Creator)) && !(this.User?.IsModerator ?? false))
|
if ((slot.Hidden || slot.SubLevel && (this.User == null && this.User != slot.Creator)) && !(this.User?.IsModerator ?? false))
|
||||||
return this.NotFound();
|
return this.NotFound();
|
||||||
|
|
||||||
|
string slotSlug = slot.GenerateSlug();
|
||||||
|
if (slug == null || slotSlug != slug)
|
||||||
|
{
|
||||||
|
return this.Redirect($"~/slot/{id}/{slotSlug}");
|
||||||
|
}
|
||||||
|
|
||||||
this.Slot = slot;
|
this.Slot = slot;
|
||||||
|
|
||||||
List<int> blockedUsers = this.User == null
|
List<int> blockedUsers = this.User == null
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@page "/user/{userId:int}"
|
@page "/user/{userId:int}/{slug?}"
|
||||||
@using System.Web
|
@using System.Web
|
||||||
@using LBPUnion.ProjectLighthouse.Extensions
|
@using LBPUnion.ProjectLighthouse.Extensions
|
||||||
@using LBPUnion.ProjectLighthouse.Localization.StringLists
|
@using LBPUnion.ProjectLighthouse.Localization.StringLists
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#nullable enable
|
#nullable enable
|
||||||
using LBPUnion.ProjectLighthouse.Configuration;
|
using LBPUnion.ProjectLighthouse.Configuration;
|
||||||
using LBPUnion.ProjectLighthouse.Database;
|
using LBPUnion.ProjectLighthouse.Database;
|
||||||
|
using LBPUnion.ProjectLighthouse.Servers.Website.Extensions;
|
||||||
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
|
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
|
using LBPUnion.ProjectLighthouse.Types.Entities.Interaction;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||||
|
@ -38,11 +39,17 @@ public class UserPage : BaseLayout
|
||||||
public UserPage(DatabaseContext database) : base(database)
|
public UserPage(DatabaseContext database) : base(database)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet([FromRoute] int userId)
|
public async Task<IActionResult> OnGet([FromRoute] int userId, string? slug)
|
||||||
{
|
{
|
||||||
this.ProfileUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == userId);
|
this.ProfileUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == userId);
|
||||||
if (this.ProfileUser == null) return this.NotFound();
|
if (this.ProfileUser == null) return this.NotFound();
|
||||||
|
|
||||||
|
string userSlug = this.ProfileUser.GenerateSlug();
|
||||||
|
if (slug == null || userSlug != slug)
|
||||||
|
{
|
||||||
|
return this.Redirect($"~/user/{userId}/{userSlug}");
|
||||||
|
}
|
||||||
|
|
||||||
bool isAuthenticated = this.User != null;
|
bool isAuthenticated = this.User != null;
|
||||||
bool isOwner = this.ProfileUser == this.User || this.User != null && this.User.IsModerator;
|
bool isOwner = this.ProfileUser == this.User || this.User != null && this.User.IsModerator;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue