diff --git a/ProjectLighthouse.Localization/General.resx b/ProjectLighthouse.Localization/General.resx
index fc10126d..c92e5f04 100644
--- a/ProjectLighthouse.Localization/General.resx
+++ b/ProjectLighthouse.Localization/General.resx
@@ -57,4 +57,7 @@
Email
+
+ Announcements
+
\ No newline at end of file
diff --git a/ProjectLighthouse.Localization/StringLists/GeneralStrings.cs b/ProjectLighthouse.Localization/StringLists/GeneralStrings.cs
index edc3eb5f..9ba858d6 100644
--- a/ProjectLighthouse.Localization/StringLists/GeneralStrings.cs
+++ b/ProjectLighthouse.Localization/StringLists/GeneralStrings.cs
@@ -15,6 +15,7 @@ public static class GeneralStrings
public static readonly TranslatableString RecentPhotos = create("recent_photos");
public static readonly TranslatableString RecentActivity = create("recent_activity");
public static readonly TranslatableString Soon = create("soon");
+ public static readonly TranslatableString Announcements = create("announcements");
private static TranslatableString create(string key) => new(TranslationAreas.General, key);
}
\ No newline at end of file
diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs
index 18b245c1..544eb7ad 100644
--- a/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs
+++ b/ProjectLighthouse.Servers.GameServer/Controllers/MessageController.cs
@@ -1,4 +1,5 @@
#nullable enable
+using System.Text;
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Extensions;
@@ -51,25 +52,22 @@ along with this program. If not, see .";
string username = await this.database.UsernameFromGameToken(token);
- string announceText = ServerConfiguration.Instance.AnnounceText;
+ StringBuilder announceText = new(ServerConfiguration.Instance.AnnounceText);
- announceText = announceText.Replace("%user", username);
- announceText = announceText.Replace("%id", token.UserId.ToString());
+ announceText.Replace("%user", username);
+ announceText.Replace("%id", token.UserId.ToString());
- return this.Ok
- (
- announceText +
- #if DEBUG
- "\n\n---DEBUG INFO---\n" +
- $"user.UserId: {token.UserId}\n" +
- $"token.UserLocation: {token.UserLocation}\n" +
- $"token.GameVersion: {token.GameVersion}\n" +
- $"token.TicketHash: {token.TicketHash}\n" +
- $"token.ExpiresAt: {token.ExpiresAt.ToString()}\n" +
- "---DEBUG INFO---" +
- #endif
- (string.IsNullOrWhiteSpace(announceText) ? "" : "\n")
- );
+ #if DEBUG
+ announceText.Append("\n\n---DEBUG INFO---\n" +
+ $"user.UserId: {token.UserId}\n" +
+ $"token.UserLocation: {token.UserLocation}\n" +
+ $"token.GameVersion: {token.GameVersion}\n" +
+ $"token.TicketHash: {token.TicketHash}\n" +
+ $"token.ExpiresAt: {token.ExpiresAt.ToString()}\n" +
+ "---DEBUG INFO---");
+ #endif
+
+ return this.Ok(announceText.ToString());
}
[HttpGet("notification")]
diff --git a/ProjectLighthouse.Servers.Website/Pages/AnnouncePage.cshtml b/ProjectLighthouse.Servers.Website/Pages/AnnouncePage.cshtml
new file mode 100644
index 00000000..06710ad2
--- /dev/null
+++ b/ProjectLighthouse.Servers.Website/Pages/AnnouncePage.cshtml
@@ -0,0 +1,67 @@
+@page "/announce"
+@using LBPUnion.ProjectLighthouse.Localization.StringLists
+@using LBPUnion.ProjectLighthouse.Types.Entities.Website
+@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.AnnouncePage
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+
+@{
+ Layout = "Layouts/BaseLayout";
+ Model.Title = Model.Translate(GeneralStrings.Announcements);
+}
+
+@if (Model.User != null && Model.User.IsAdmin)
+{
+
+
Post New Announcement
+ @if (!string.IsNullOrWhiteSpace(Model.Error))
+ {
+ @await Html.PartialAsync("Partials/ErrorModalPartial", (Model.Translate(GeneralStrings.Error), Model.Error), ViewData)
+ }
+
+
+}
+
+@if (Model.Announcements.Any())
+{
+ @foreach (WebsiteAnnouncementEntity announcement in Model.Announcements)
+ {
+
+
@announcement.Title
+
+ @announcement.Content
+
+
+ @if (Model.User != null && Model.User.IsAdmin)
+ {
+
+ }
+
+ }
+}
+else
+{
+
+
There are no announcements to display.
+
+}
\ No newline at end of file
diff --git a/ProjectLighthouse.Servers.Website/Pages/AnnouncePage.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/AnnouncePage.cshtml.cs
new file mode 100644
index 00000000..bf4175d4
--- /dev/null
+++ b/ProjectLighthouse.Servers.Website/Pages/AnnouncePage.cshtml.cs
@@ -0,0 +1,82 @@
+#nullable enable
+
+using LBPUnion.ProjectLighthouse.Configuration;
+using LBPUnion.ProjectLighthouse.Database;
+using LBPUnion.ProjectLighthouse.Helpers;
+using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
+using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
+using LBPUnion.ProjectLighthouse.Types.Entities.Website;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages;
+
+public class AnnouncePage : BaseLayout
+{
+ public AnnouncePage(DatabaseContext database) : base(database)
+ { }
+
+ public List Announcements { get; set; } = new();
+ public string Error { get; set; } = "";
+
+ public async Task OnGet()
+ {
+ this.Announcements = await this.Database.WebsiteAnnouncements
+ .OrderByDescending(a => a.AnnouncementId)
+ .ToListAsync();
+
+ return this.Page();
+ }
+
+ public async Task OnPost([FromForm] string title, [FromForm] string content)
+ {
+ UserEntity? user = this.Database.UserFromWebRequest(this.Request);
+ if (user == null) return this.BadRequest();
+ if (!user.IsAdmin) return this.BadRequest();
+
+ if (string.IsNullOrWhiteSpace(title) || string.IsNullOrWhiteSpace(content))
+ {
+ this.Error = "Invalid form data, please ensure all fields are filled out.";
+ return this.Page();
+ }
+
+ WebsiteAnnouncementEntity announcement = new()
+ {
+ Title = title,
+ Content = content,
+ };
+
+ await this.Database.WebsiteAnnouncements.AddAsync(announcement);
+ await this.Database.SaveChangesAsync();
+
+ if (DiscordConfiguration.Instance.DiscordIntegrationEnabled)
+ {
+ string truncatedAnnouncement = content.Length > 250
+ ? content[..250] + $"... [read more]({ServerConfiguration.Instance.ExternalUrl}/announce)"
+ : content;
+
+ await WebhookHelper.SendWebhook($":mega: {title}",
+ truncatedAnnouncement);
+ }
+
+ return this.RedirectToPage();
+ }
+
+ public async Task OnPostDelete(int id)
+ {
+ UserEntity? user = this.Database.UserFromWebRequest(this.Request);
+ if (user == null) return this.BadRequest();
+ if (!user.IsAdmin) return this.BadRequest();
+
+ WebsiteAnnouncementEntity? announcement = await this.Database.WebsiteAnnouncements
+ .Where(a => a.AnnouncementId == id)
+ .FirstOrDefaultAsync();
+
+ if (announcement == null) return this.BadRequest();
+
+ this.Database.WebsiteAnnouncements.Remove(announcement);
+ await this.Database.SaveChangesAsync();
+
+ return this.RedirectToPage();
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml.cs b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml.cs
index 5c1ce0f8..3d81280b 100644
--- a/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml.cs
+++ b/ProjectLighthouse.Servers.Website/Pages/Layouts/BaseLayout.cshtml.cs
@@ -33,6 +33,8 @@ public class BaseLayout : PageModel
this.NavigationItems.Add(new PageNavigationItem(BaseLayoutStrings.HeaderUsers, "/users/0", "user friends"));
this.NavigationItems.Add(new PageNavigationItem(BaseLayoutStrings.HeaderPhotos, "/photos/0", "camera"));
this.NavigationItems.Add(new PageNavigationItem(BaseLayoutStrings.HeaderSlots, "/slots/0", "globe americas"));
+
+ this.NavigationItemsRight.Add(new PageNavigationItem(GeneralStrings.Announcements, "/announce", "bullhorn"));
}
public new UserEntity? User {
diff --git a/ProjectLighthouse.Tests.GameApiTests/Unit/Controllers/MessageControllerTests.cs b/ProjectLighthouse.Tests.GameApiTests/Unit/Controllers/MessageControllerTests.cs
index 8728a613..0c14a9df 100644
--- a/ProjectLighthouse.Tests.GameApiTests/Unit/Controllers/MessageControllerTests.cs
+++ b/ProjectLighthouse.Tests.GameApiTests/Unit/Controllers/MessageControllerTests.cs
@@ -82,7 +82,7 @@ along with this program. If not, see ." + "\nuni
ServerConfiguration.Instance.AnnounceText = "you are now logged in as %user (id: %id)";
- const string expected = "you are now logged in as unittest (id: 1)\n";
+ const string expected = "you are now logged in as unittest (id: 1)";
IActionResult result = await messageController.Announce();
diff --git a/ProjectLighthouse.Tests.WebsiteTests/Integration/AdminTests.cs b/ProjectLighthouse.Tests.WebsiteTests/Integration/AdminTests.cs
index ec8d9782..79d15561 100644
--- a/ProjectLighthouse.Tests.WebsiteTests/Integration/AdminTests.cs
+++ b/ProjectLighthouse.Tests.WebsiteTests/Integration/AdminTests.cs
@@ -14,7 +14,7 @@ namespace ProjectLighthouse.Tests.WebsiteTests.Integration;
[Trait("Category", "Integration")]
public class AdminTests : LighthouseWebTest
{
- private const string adminPanelButtonXPath = "/html/body/div/header/div/div/div/a[1]";
+ private const string adminPanelButtonXPath = "/html/body/div/header/div/div/div/a[2]";
[Fact]
public async Task ShouldShowAdminPanelButtonWhenAdmin()
diff --git a/ProjectLighthouse/Database/DatabaseContext.cs b/ProjectLighthouse/Database/DatabaseContext.cs
index 830ee726..2bd56296 100644
--- a/ProjectLighthouse/Database/DatabaseContext.cs
+++ b/ProjectLighthouse/Database/DatabaseContext.cs
@@ -5,6 +5,7 @@ using LBPUnion.ProjectLighthouse.Types.Entities.Maintenance;
using LBPUnion.ProjectLighthouse.Types.Entities.Moderation;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
+using LBPUnion.ProjectLighthouse.Types.Entities.Website;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Database;
@@ -61,6 +62,10 @@ public partial class DatabaseContext : DbContext
#region Misc
public DbSet CompletedMigrations { get; set; }
#endregion
+
+ #region Website
+ public DbSet WebsiteAnnouncements { get; set; }
+ #endregion
#endregion
diff --git a/ProjectLighthouse/Migrations/20230620211613_AddWebAnnouncementsToDb.cs b/ProjectLighthouse/Migrations/20230620211613_AddWebAnnouncementsToDb.cs
new file mode 100644
index 00000000..592f9ee3
--- /dev/null
+++ b/ProjectLighthouse/Migrations/20230620211613_AddWebAnnouncementsToDb.cs
@@ -0,0 +1,48 @@
+using LBPUnion.ProjectLighthouse.Database;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace ProjectLighthouse.Migrations
+{
+ [DbContext(typeof(DatabaseContext))]
+ [Migration("20230620211613_AddWebAnnouncementsToDb")]
+ public partial class AddWebAnnouncementsToDb : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "WebsiteAnnouncements",
+ columns: table => new
+ {
+ AnnouncementId = table.Column(
+ type: "int",
+ nullable: false)
+ .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
+ Title = table.Column(
+ type: "longtext",
+ nullable: false,
+ defaultValue: "")
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Content = table.Column(
+ type: "longtext",
+ nullable: false,
+ defaultValue: "")
+ .Annotation("MySql:CharSet", "utf8mb4")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_WebsiteAnnouncements", x => x.AnnouncementId);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "WebsiteAnnouncements");
+ }
+ }
+}
diff --git a/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs
index f21666ba..847b390b 100644
--- a/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs
+++ b/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs
@@ -16,7 +16,7 @@ namespace ProjectLighthouse.Migrations
{
#pragma warning disable 612, 618
modelBuilder
- .HasAnnotation("ProductVersion", "7.0.4")
+ .HasAnnotation("ProductVersion", "7.0.7")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Entities.Interaction.HeartedLevelEntity", b =>
@@ -1024,6 +1024,23 @@ namespace ProjectLighthouse.Migrations
b.ToTable("WebTokens");
});
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Entities.Website.WebsiteAnnouncementEntity", b =>
+ {
+ b.Property("AnnouncementId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Content")
+ .HasColumnType("longtext");
+
+ b.Property("Title")
+ .HasColumnType("longtext");
+
+ b.HasKey("Identifier");
+
+ b.ToTable("WebsiteAnnouncements");
+ });
+
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Entities.Interaction.HeartedLevelEntity", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.Types.Entities.Level.SlotEntity", "Slot")
diff --git a/ProjectLighthouse/Types/Entities/Website/WebsiteAnnouncementEntity.cs b/ProjectLighthouse/Types/Entities/Website/WebsiteAnnouncementEntity.cs
new file mode 100644
index 00000000..26c42488
--- /dev/null
+++ b/ProjectLighthouse/Types/Entities/Website/WebsiteAnnouncementEntity.cs
@@ -0,0 +1,13 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace LBPUnion.ProjectLighthouse.Types.Entities.Website;
+
+public class WebsiteAnnouncementEntity
+{
+ [Key]
+ public int AnnouncementId { get; set; }
+
+ public string Title { get; set; }
+
+ public string Content { get; set; }
+}
\ No newline at end of file