Merge branch 'main' into web-tests

This commit is contained in:
jvyden 2021-12-24 14:22:18 -05:00 committed by GitHub
commit d96c544d71
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 151 additions and 16 deletions

View file

@ -53,6 +53,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
photo.CreatorId = user.UserId; photo.CreatorId = user.UserId;
photo.Creator = user; photo.Creator = user;
if (photo.Subjects.Count > 4)
{
return this.BadRequest();
}
foreach (PhotoSubject subject in photo.Subjects) foreach (PhotoSubject subject in photo.Subjects)
{ {
subject.User = await this.database.Users.FirstOrDefaultAsync(u => u.Username == subject.Username); subject.User = await this.database.Users.FirstOrDefaultAsync(u => u.Username == subject.Username);
@ -67,6 +72,17 @@ namespace LBPUnion.ProjectLighthouse.Controllers
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();
// Check for duplicate photo subjects
List<int> subjectUserIds = new(4);
foreach (PhotoSubject subject in photo.Subjects)
{
if (subjectUserIds.Contains(subject.UserId))
{
return this.BadRequest();
}
subjectUserIds.Add(subject.UserId);
}
photo.PhotoSubjectIds = photo.Subjects.Select(subject => subject.PhotoSubjectId.ToString()).ToArray(); photo.PhotoSubjectIds = photo.Subjects.Select(subject => subject.PhotoSubjectId.ToString()).ToArray();
// photo.Slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId); // photo.Slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId);

View file

@ -0,0 +1,15 @@
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
using Microsoft.Net.Http.Headers;
namespace LBPUnion.ProjectLighthouse.Helpers.Extensions
{
// yoinked and adapted from https://stackoverflow.com/a/68641796
public static class RequestExtensions
{
private static readonly Regex mobileCheck = new
("Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled);
public static bool IsMobile(this HttpRequest request) => mobileCheck.IsMatch(request.Headers[HeaderNames.UserAgent].ToString());
}
}

View file

@ -5,5 +5,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers
public static class TimestampHelper public static class TimestampHelper
{ {
public static long Timestamp => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; public static long Timestamp => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
public static long TimestampMillis => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds;
} }
} }

View file

@ -13,7 +13,7 @@ namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs
{ {
private readonly Database database = new(); private readonly Database database = new();
public string Name() => "Cleanup Broken Photos"; public string Name() => "Cleanup Broken Photos";
public string Description() => "Deletes all photos that have missing assets."; public string Description() => "Deletes all photos that have missing assets or invalid photo subjects.";
[SuppressMessage("ReSharper", "LoopCanBePartlyConvertedToQuery")] [SuppressMessage("ReSharper", "LoopCanBePartlyConvertedToQuery")]
public async Task Run() public async Task Run()
@ -23,6 +23,8 @@ namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs
bool hashNullOrEmpty = false; bool hashNullOrEmpty = false;
bool noHashesExist = false; bool noHashesExist = false;
bool largeHashIsInvalidFile = false; bool largeHashIsInvalidFile = false;
bool tooManyPhotoSubjects = false;
bool duplicatePhotoSubjects = false;
hashNullOrEmpty = string.IsNullOrEmpty hashNullOrEmpty = string.IsNullOrEmpty
(photo.LargeHash) || (photo.LargeHash) ||
@ -50,6 +52,23 @@ namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs
goto removePhoto; goto removePhoto;
} }
if (photo.Subjects.Count > 4)
{
tooManyPhotoSubjects = true;
goto removePhoto;
}
List<int> subjectUserIds = new(4);
foreach (PhotoSubject subject in photo.Subjects)
{
if (subjectUserIds.Contains(subject.UserId))
{
duplicatePhotoSubjects = true;
goto removePhoto;
}
subjectUserIds.Add(subject.UserId);
}
continue; continue;
removePhoto: removePhoto:
@ -59,7 +78,9 @@ namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs
$"Removing photo (id: {photo.PhotoId}): " + $"Removing photo (id: {photo.PhotoId}): " +
$"{nameof(hashNullOrEmpty)}: {hashNullOrEmpty}, " + $"{nameof(hashNullOrEmpty)}: {hashNullOrEmpty}, " +
$"{nameof(noHashesExist)}: {noHashesExist}, " + $"{nameof(noHashesExist)}: {noHashesExist}, " +
$"{nameof(largeHashIsInvalidFile)}: {largeHashIsInvalidFile}" $"{nameof(largeHashIsInvalidFile)}: {largeHashIsInvalidFile}, " +
$"{nameof(tooManyPhotoSubjects)}: {tooManyPhotoSubjects}" +
$"{nameof(duplicatePhotoSubjects)}: {duplicatePhotoSubjects}"
); );
this.database.Photos.Remove(photo); this.database.Photos.Remove(photo);

View file

@ -1,4 +1,5 @@
@using LBPUnion.ProjectLighthouse.Helpers @using LBPUnion.ProjectLighthouse.Helpers
@using LBPUnion.ProjectLighthouse.Helpers.Extensions
@using LBPUnion.ProjectLighthouse.Types @using LBPUnion.ProjectLighthouse.Types
@using LBPUnion.ProjectLighthouse.Types.Settings @using LBPUnion.ProjectLighthouse.Types.Settings
@model LBPUnion.ProjectLighthouse.Pages.Layouts.BaseLayout @model LBPUnion.ProjectLighthouse.Pages.Layouts.BaseLayout
@ -26,6 +27,9 @@
} }
Model.NavigationItemsRight.Add(new PageNavigationItem("Log out", "/logout", "user alternate slash")); // should always be last Model.NavigationItemsRight.Add(new PageNavigationItem("Log out", "/logout", "user alternate slash")); // should always be last
} }
Model.IsMobile = Model.Request.IsMobile();
long timeStarted = TimestampHelper.TimestampMillis;
} }
<!DOCTYPE html> <!DOCTYPE html>
@ -48,10 +52,20 @@
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest"> <link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5"> <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#008cff">
<meta name="msapplication-TileColor" content="#da532c"> <meta name="msapplication-TileColor" content="#008cff">
<meta name="theme-color" content="#ffffff">
@* Embed Stuff *@
<meta name="theme-color" data-react-helmet="true" content="#008cff">
<meta content="Project Lighthouse - @Model.Title" property="og:title">
@if (!string.IsNullOrEmpty(Model.Description))
{
<meta content="@Model.Description" property="og:description">
}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@* Google Analytics *@
@if (ServerSettings.Instance.GoogleAnalyticsEnabled) @if (ServerSettings.Instance.GoogleAnalyticsEnabled)
{ {
<!-- Global site tag (gtag.js) - Google Analytics --> <!-- Global site tag (gtag.js) - Google Analytics -->
@ -81,14 +95,21 @@
<header class="lighthouse-header"> <header class="lighthouse-header">
<div class="ui attached menu"> <div class="ui attached menu">
<div class="ui container"> <div class="ui container">
@{
string mobileIconStyle = Model.IsMobile ? "margin-right: 0;" : "";
}
@foreach (PageNavigationItem navigationItem in Model!.NavigationItems) @foreach (PageNavigationItem navigationItem in Model!.NavigationItems)
{ {
<a class="item" href="@navigationItem.Url"> <a class="item" href="@navigationItem.Url">
@if (navigationItem.Icon != null) @if (navigationItem.Icon != null)
{ {
<i class="@navigationItem.Icon icon"></i> <i class="@navigationItem.Icon icon" style="@mobileIconStyle"></i>
}
@if (!Model.IsMobile)
{
@navigationItem.Name
} }
@navigationItem.Name
</a> </a>
} }
<div class="right menu"> <div class="right menu">
@ -97,9 +118,13 @@
<a class="item" href="@navigationItem.Url"> <a class="item" href="@navigationItem.Url">
@if (navigationItem.Icon != null) @if (navigationItem.Icon != null)
{ {
<i class="@navigationItem.Icon icon"></i> <i class="@navigationItem.Icon icon" style="@mobileIconStyle"></i>
}
@if (!Model.IsMobile)
{
@navigationItem.Name
} }
@navigationItem.Name
</a> </a>
} }
</div> </div>
@ -143,6 +168,46 @@
} }
</div> </div>
</div> </div>
@if (ServerStatics.IsDebug)
{
<div class="ui red attached inverted segment">
<div class="ui container">
<button type="button" class="ui inverted button collapsible">
<b>Show/Hide Debug Info</b>
</button>
<div style="display:none" id="lighthouse-debug-info">
<br>
<p>Model.IsMobile: @Model.IsMobile</p>
<p>Model.Title: @Model.Title</p>
<p>Model.Description: @Model.Description</p>
<p>Model.User.UserId: @(Model.User?.UserId.ToString() ?? "(not logged in)")</p>
<p>Render time: ~@(TimestampHelper.TimestampMillis - timeStarted)ms</p>
</div>
</div>
</div>
<script>
const collapsible = document.getElementsByClassName("collapsible");
for (let i = 0; i < collapsible.length; i++)
{
collapsible[i].addEventListener("click", function()
{
this.classList.toggle("active");
const content = this.nextElementSibling;
if (content.style.display === "block")
{
content.style.display = "none";
}
else
{
content.style.display = "block";
}
});
}
</script>
}
</footer> </footer>
</div> </div>
</body> </body>

View file

@ -7,6 +7,13 @@ namespace LBPUnion.ProjectLighthouse.Pages.Layouts
{ {
public class BaseLayout : PageModel public class BaseLayout : PageModel
{ {
public BaseLayout(Database database)
{
this.Database = database;
}
public bool IsMobile;
public readonly Database Database; public readonly Database Database;
public readonly List<PageNavigationItem> NavigationItems = new() public readonly List<PageNavigationItem> NavigationItems = new()
@ -21,14 +28,10 @@ namespace LBPUnion.ProjectLighthouse.Pages.Layouts
public bool ShowTitleInPage = true; public bool ShowTitleInPage = true;
public string Title = string.Empty; public string Title = string.Empty;
public string Description = string.Empty;
private User? user; private User? user;
public BaseLayout(Database database)
{
this.Database = database;
}
public new User? User { public new User? User {
get { get {
if (this.user != null) return this.user; if (this.user != null) return this.user;

View file

@ -3,8 +3,10 @@
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";
Model.Title = Model.Slot.Name;
Model.ShowTitleInPage = false; Model.ShowTitleInPage = false;
Model.Title = Model.Slot.Name;
Model.Description = Model.Slot.Description;
} }
@await Html.PartialAsync("Partials/SlotCardPartial", Model.Slot, new ViewDataDictionary(ViewData) @await Html.PartialAsync("Partials/SlotCardPartial", Model.Slot, new ViewDataDictionary(ViewData)

View file

@ -8,8 +8,10 @@
@{ @{
Layout = "Layouts/BaseLayout"; Layout = "Layouts/BaseLayout";
Model.Title = Model.ProfileUser!.Username + "'s user page";
Model.ShowTitleInPage = false; Model.ShowTitleInPage = false;
Model.Title = Model.ProfileUser!.Username + "'s user page";
Model.Description = Model.ProfileUser!.Biography;
} }
<div class="ui grid"> <div class="ui grid">

View file

@ -16,3 +16,6 @@ div.statsUnderTitle > span {
margin-right: 5px; margin-right: 5px;
} }
#lighthouse-debug-info > p {
margin-bottom: 1px;
}

View file

@ -27,5 +27,11 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings
} }
public static bool IsUnitTesting => AppDomain.CurrentDomain.GetAssemblies().Any(assembly => assembly.FullName.StartsWith("xunit")); public static bool IsUnitTesting => AppDomain.CurrentDomain.GetAssemblies().Any(assembly => assembly.FullName.StartsWith("xunit"));
#if DEBUG
public static readonly bool IsDebug = true;
#else
public static readonly bool IsDebug = false;
#endif
} }
} }