Skip to content

Commit

Permalink
fix #1709
Browse files Browse the repository at this point in the history
  • Loading branch information
vadosnaprimer committed Jan 5, 2024
1 parent 735a1fe commit aeee02d
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 137 deletions.
9 changes: 9 additions & 0 deletions TASVideos.Core/Settings/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public class AppSettings

public Connections ConnectionStrings { get; set; } = new();

public SubmissionRateLimit SubmissionRate { get; set; } = new ();

public IrcConnection Irc { get; set; } = new();
public DiscordConnection Discord { get; set; } = new();

Expand All @@ -24,6 +26,13 @@ public class AppSettings
// Minimum number of hours before a judge can set a submission to accepted/rejected
public int MinimumHoursBeforeJudgment { get; set; }

// User is only allowed to submit X submissions in Y days
public class SubmissionRateLimit
{
public int Submissions { get; set; }
public int Days { get; set; }
}

public class IrcConnection : DistributorConnection
{
public string Server { get; set; } = "";
Expand Down
287 changes: 152 additions & 135 deletions TASVideos/Pages/Submissions/Submit.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -5,143 +5,160 @@
ViewData.SetTitle("Submit Movie");
var notSupportedError = ViewData.ModelState.Values.Any(v => v.Errors.Any(e => e.ErrorMessage.Contains("not currently supported"))); // TODO: a less brittle check
var parseErrors = !notSupportedError && ViewData.ModelState.Keys.Any(e => e == "Parser");
var submissionAllowed = Model.SubmissionAllowed(User.GetUserId());
var notice = Model.Notice(User.GetUserId());
}
<info-alert dismissible="true">
@await Component.RenderWiki(SystemWiki.SubmitMovieHeader)
</info-alert>
<hr />
<div asp-validation-summary="ModelOnly" class="alert alert-danger" role="alert"></div>
<warning-alert dismissible="true" condition="notSupportedError">
@await Component.RenderWiki(SystemWiki.SupportedMovieTypes)
</warning-alert>
<danger-alert dismissible="true" condition="parseErrors">
@await Component.RenderWiki(SystemWiki.SubmissionZipFailure)
</danger-alert>
<span id="backup-submission-determinator" class="d-none">@Model.BackupSubmissionDeterminator</span>
<form method="post" enctype="multipart/form-data">
<row>
<column lg="6">
<fieldset>
<label asp-for="Create.MovieFile" class="form-control-label"></label>
<input asp-for="Create.MovieFile" class="form-control" />
<div>@Html.DescriptionFor(m => m.Create.MovieFile)</div>
<span asp-validation-for="Create.MovieFile" class="text-danger"></span>
</fieldset>
</column>
<column lg="6">
<fieldset>
<label asp-for="Create.Authors" class="form-control-label"></label>
<string-list asp-for="Create.Authors" />
<span asp-validation-for="Create.Authors" class="text-danger"></span>
</fieldset>
<fieldset>
<label asp-for="Create.AdditionalAuthors" class="form-control-label"></label>
<input asp-for="Create.AdditionalAuthors" class="form-control" />
<div>@Html.DescriptionFor(m => m.Create.AdditionalAuthors)</div>
<span asp-validation-for="Create.AdditionalAuthors" class="text-danger"></span>
</fieldset>
</column>
</row>
<row>
<column lg="6">
<fieldset>
<label asp-for="Create.GameVersion" class="form-control-label"></label>
<input asp-for="Create.GameVersion" class="form-control" placeholder="@Html.DescriptionFor(m => m.Create.GameVersion)" />
<span asp-validation-for="Create.GameVersion" class="text-danger"></span>
</fieldset>
<fieldset>
<label asp-for="Create.GameName" class="form-control-label"></label>
<input asp-for="Create.GameName" class="form-control" placeholder="@Html.DescriptionFor(m => m.Create.GameName)" />
<span asp-validation-for="Create.GameName" class="text-danger"></span>
</fieldset>
<fieldset>
<label asp-for="Create.Emulator" class="form-control-label"></label>
<input asp-for="Create.Emulator" spellcheck="false" class="form-control" placeholder="@Html.DescriptionFor(m => m.Create.Emulator)" />
<span asp-validation-for="Create.Emulator" class="text-danger"></span>
</fieldset>
</column>
<column lg="6">
<fieldset>
<label asp-for="Create.Branch" class="form-control-label"></label>
<input asp-for="Create.Branch" class="form-control" placeholder="@Html.DescriptionFor(m => m.Create.Branch)" />
<span asp-validation-for="Create.Branch" class="text-danger"></span>
</fieldset>
<fieldset>
<label asp-for="Create.RomName" class="form-control-label"></label>
<input asp-for="Create.RomName" class="form-control" placeholder="@Html.DescriptionFor(m => m.Create.RomName)" />
<span asp-validation-for="Create.RomName" class="text-danger"></span>
</fieldset>
<fieldset>
<label asp-for="Create.EncodeEmbedLink" class="form-control-label"></label>
<input asp-for="Create.EncodeEmbedLink" class="form-control" placeholder="@Html.DescriptionFor(m => m.Create.EncodeEmbedLink)" />
<span asp-validation-for="Create.EncodeEmbedLink" class="text-danger"></span>
</fieldset>
</column>
</row>
<fullrow>
<fieldset>
<label asp-for="Create.Markup" class="form-control-label"></label>
<span>
@await Component.RenderWiki(SystemWiki.SubmissionImportant)
</span>
<partial name="_WikiEditHelper" model="@("Create_Markup")" />
<textarea asp-for="Create.Markup" rows="12" class="form-control wiki-edit backup-content" data-backup-key="backup-submission" placeholder="Enter your __wiki markup__ here..."></textarea>
<span asp-validation-for="Create.Markup" class="text-danger"></span>
<fullrow id="backup-restore" class="d-none mt-2">
<button id="backup-restore-button" type="button" class="btn btn-secondary">Restore Text</button>
<label class="text-body-tertiary">from <span id="backup-time"></span></label>

@{
if (!submissionAllowed)
{
<info-alert>
<row>
<div class="col-12">@notice[0]</div>
<div class="col-12">@notice[1]</div>
</row>
</info-alert>
}
else
{
<info-alert dismissible="true">
@await Component.RenderWiki(SystemWiki.SubmitMovieHeader)
</info-alert>
<hr />
<div asp-validation-summary="ModelOnly" class="alert alert-danger" role="alert"></div>
<warning-alert dismissible="true" condition="notSupportedError">
@await Component.RenderWiki(SystemWiki.SupportedMovieTypes)
</warning-alert>
<danger-alert dismissible="true" condition="parseErrors">
@await Component.RenderWiki(SystemWiki.SubmissionZipFailure)
</danger-alert>
<span id="backup-submission-determinator" class="d-none">@Model.BackupSubmissionDeterminator</span>
<form method="post" enctype="multipart/form-data">
<row>
<column lg="6">
<fieldset>
<label asp-for="Create.MovieFile" class="form-control-label"></label>
<input asp-for="Create.MovieFile" class="form-control" />
<div>@Html.DescriptionFor(m => m.Create.MovieFile)</div>
<span asp-validation-for="Create.MovieFile" class="text-danger"></span>
</fieldset>
</column>
<column lg="6">
<fieldset>
<label asp-for="Create.Authors" class="form-control-label"></label>
<string-list asp-for="Create.Authors" />
<span asp-validation-for="Create.Authors" class="text-danger"></span>
</fieldset>
<fieldset>
<label asp-for="Create.AdditionalAuthors" class="form-control-label"></label>
<input asp-for="Create.AdditionalAuthors" class="form-control" />
<div>@Html.DescriptionFor(m => m.Create.AdditionalAuthors)</div>
<span asp-validation-for="Create.AdditionalAuthors" class="text-danger"></span>
</fieldset>
</column>
</row>
<row>
<column lg="6">
<fieldset>
<label asp-for="Create.GameVersion" class="form-control-label"></label>
<input asp-for="Create.GameVersion" class="form-control" placeholder="@Html.DescriptionFor(m => m.Create.GameVersion)" />
<span asp-validation-for="Create.GameVersion" class="text-danger"></span>
</fieldset>
<fieldset>
<label asp-for="Create.GameName" class="form-control-label"></label>
<input asp-for="Create.GameName" class="form-control" placeholder="@Html.DescriptionFor(m => m.Create.GameName)" />
<span asp-validation-for="Create.GameName" class="text-danger"></span>
</fieldset>
<fieldset>
<label asp-for="Create.Emulator" class="form-control-label"></label>
<input asp-for="Create.Emulator" spellcheck="false" class="form-control" placeholder="@Html.DescriptionFor(m => m.Create.Emulator)" />
<span asp-validation-for="Create.Emulator" class="text-danger"></span>
</fieldset>
</column>
<column lg="6">
<fieldset>
<label asp-for="Create.Branch" class="form-control-label"></label>
<input asp-for="Create.Branch" class="form-control" placeholder="@Html.DescriptionFor(m => m.Create.Branch)" />
<span asp-validation-for="Create.Branch" class="text-danger"></span>
</fieldset>
<fieldset>
<label asp-for="Create.RomName" class="form-control-label"></label>
<input asp-for="Create.RomName" class="form-control" placeholder="@Html.DescriptionFor(m => m.Create.RomName)" />
<span asp-validation-for="Create.RomName" class="text-danger"></span>
</fieldset>
<fieldset>
<label asp-for="Create.EncodeEmbedLink" class="form-control-label"></label>
<input asp-for="Create.EncodeEmbedLink" class="form-control" placeholder="@Html.DescriptionFor(m => m.Create.EncodeEmbedLink)" />
<span asp-validation-for="Create.EncodeEmbedLink" class="text-danger"></span>
</fieldset>
</column>
</row>
<fullrow>
<fieldset>
<label asp-for="Create.Markup" class="form-control-label"></label>
<span>
@await Component.RenderWiki(SystemWiki.SubmissionImportant)
</span>
<partial name="_WikiEditHelper" model="@("Create_Markup")" />
<textarea asp-for="Create.Markup" rows="12" class="form-control wiki-edit backup-content" data-backup-key="backup-submission" placeholder="Enter your __wiki markup__ here..."></textarea>
<span asp-validation-for="Create.Markup" class="text-danger"></span>
<fullrow id="backup-restore" class="d-none mt-2">
<button id="backup-restore-button" type="button" class="btn btn-secondary">Restore Text</button>
<label class="text-body-tertiary">from <span id="backup-time"></span></label>
</fullrow>
<div>
<button id="prefill-btn" type="button" class="btn btn-secondary mt-2">Prefill comments</button>
</div>
</fieldset>
</fullrow>
<div>
<button id="prefill-btn" type="button" class="btn btn-secondary mt-2">Prefill comments</button>
</div>
</fieldset>
</fullrow>
<row class="text-center justify-content-center mb-1 fs-6">
<div class="col-auto">
<div class="form-check">
<input asp-for="Create.AgreeToInstructions" class="form-check-input" />
<label asp-for="Create.AgreeToInstructions" class="form-check-label">I have read and followed the TASVideos <a href="/SubmissionInstructions">Submission Instructions</a>.</label>
<br />
<span asp-validation-for="Create.AgreeToInstructions" class="text-danger"></span>
</div>
</div>
</row>
<row class="text-center justify-content-center mb-3 fs-6">
<div class="col-auto">
<div class="form-check">
<input asp-for="Create.AgreeToLicense" class="form-check-input" />
<label asp-for="Create.AgreeToLicense" class="form-check-label">I agree to publishing this content under the <a href="https://creativecommons.org/licenses/by/2.0/">Creative Commons Attribution 2.0</a> license.</label>
<br />
<span asp-validation-for="Create.AgreeToLicense" class="text-danger"></span>
</div>
</div>
</row>
<fullrow class="text-center">
<button id="preview-button" type="button" class="btn btn-secondary"><i class="fa fa-eye"></i> Preview</button>
<submit-button id="submit-btn" class="btn btn-primary border border-warning @(Model.Create.Markup.Length > 0 ? "" : "d-none")">Submit</submit-button>
</fullrow>
</form>
<row class="text-center justify-content-center mb-1 fs-6">
<div class="col-auto">
<div class="form-check">
<input asp-for="Create.AgreeToInstructions" class="form-check-input" />
<label asp-for="Create.AgreeToInstructions" class="form-check-label">I have read and followed the TASVideos <a href="/SubmissionInstructions">Submission Instructions</a>.</label>
<br />
<span asp-validation-for="Create.AgreeToInstructions" class="text-danger"></span>
</div>
</div>
</row>
<row class="text-center justify-content-center mb-3 fs-6">
<div class="col-auto">
<div class="form-check">
<input asp-for="Create.AgreeToLicense" class="form-check-input" />
<label asp-for="Create.AgreeToLicense" class="form-check-label">I agree to publishing this content under the <a href="https://creativecommons.org/licenses/by/2.0/">Creative Commons Attribution 2.0</a> license.</label>
<br />
<span asp-validation-for="Create.AgreeToLicense" class="text-danger"></span>
</div>
</div>
</row>
<fullrow class="text-center">
<button id="preview-button" type="button" class="btn btn-secondary"><i class="fa fa-eye"></i> Preview</button>
<submit-button id="submit-btn" class="btn btn-primary border border-warning @(Model.Create.Markup.Length > 0 ? "" : "d-none")">Submit</submit-button>
</fullrow>
</form>

<partial name="_PreviewWindow" model="@((Html.IdFor(m => m.Create.Markup), "/Wiki/Preview"))" />
@section Scripts {
<partial name="_ValidationScriptsPartial" />
<script>
document.getElementById('prefill-btn').onclick = function () {
const markup = document.getElementById('@Html.IdFor(m => m.Create.Markup)').value;
if (markup) {
return;
}
<partial name="_PreviewWindow" model="@((Html.IdFor(m => m.Create.Markup), "/Wiki/Preview"))" />
@section Scripts {
<partial name="_ValidationScriptsPartial" />
<script>
document.getElementById('prefill-btn').onclick = function () {
const markup = document.getElementById('@Html.IdFor(m => m.Create.Markup)').value;
if (markup) {
return;
}
fetch("/Submissions/Submit?handler=PrefillText")
.then(handleFetchErrors)
.then(r => r.json())
.then(data => {
document.getElementById('@Html.IdFor(m => m.Create.Markup)').value = data.text;
fetch("/Submissions/Submit?handler=PrefillText")
.then(handleFetchErrors)
.then(r => r.json())
.then(data => {
document.getElementById('@Html.IdFor(m => m.Create.Markup)').value = data.text;
});
};
document.getElementById('preview-button').addEventListener('click', function () {
document.getElementById('submit-btn').classList.remove('d-none');
});
};
document.getElementById('preview-button').addEventListener('click', function () {
document.getElementById('submit-btn').classList.remove('d-none');
});
</script>
<script src="~/js/backup-text.js"></script>
}
</script>
<script src="~/js/backup-text.js"></script>
}
}
}
29 changes: 28 additions & 1 deletion TASVideos/Pages/Submissions/Submit.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using TASVideos.Core.Services.ExternalMediaPublisher;
using TASVideos.Core.Services.Wiki;
using TASVideos.Core.Services.Youtube;
using TASVideos.Core.Settings;
using TASVideos.Data;
using TASVideos.Data.Entity;
using TASVideos.MovieParsers;
Expand All @@ -23,6 +24,8 @@ public class SubmitModel : BasePageModel
private readonly IYoutubeSync _youtubeSync;
private readonly IMovieFormatDeprecator _deprecator;
private readonly IQueueService _queueService;
private readonly AppSettings _settings;
private DateTime _earliestTimestamp;

public SubmitModel(
ApplicationDbContext db,
Expand All @@ -33,7 +36,8 @@ public SubmitModel(
ITASVideoAgent tasVideoAgent,
IYoutubeSync youtubeSync,
IMovieFormatDeprecator deprecator,
IQueueService queueService)
IQueueService queueService,
AppSettings settings)
{
_db = db;
_publisher = publisher;
Expand All @@ -44,6 +48,7 @@ public SubmitModel(
_youtubeSync = youtubeSync;
_deprecator = deprecator;
_queueService = queueService;
_settings = settings;
}

[BindProperty]
Expand Down Expand Up @@ -183,4 +188,26 @@ private async Task ValidateModel()
}
}
}

public string[] Notice(int userId) => new string[]
{
"We limit submissions to " +
_settings.SubmissionRate.Submissions +
" in " +
_settings.SubmissionRate.Days +
" days per user. ",
"You will be able to submit again on " +
_earliestTimestamp.AddDays(_settings.SubmissionRate.Days)
};

public bool SubmissionAllowed(int userId)
{
_earliestTimestamp = _db.Submissions
.Where(s => s.Submitter != null && s.SubmitterId == userId)

This comment has been minimized.

Copy link
@adelikat

adelikat Jan 5, 2024

Collaborator

This queries all the submissions by the author, most people don't have so many but some people do (like me!)
More efficient is to query all the submissions in the last 24 hours, then do your logic against those results

This comment has been minimized.

Copy link
@adelikat

adelikat Jan 5, 2024

Collaborator

Correction: not last 24 hours, last x days based on the settings

This comment has been minimized.

Copy link
@vadosnaprimer

vadosnaprimer Jan 5, 2024

Author Collaborator

In the last week in my case (or however many days we end up having), but I don't know how to apply that limit right here.

Also I pick an element at a fixed index below, forgetting it may not even exist.

This comment has been minimized.

Copy link
@vadosnaprimer

vadosnaprimer Jan 5, 2024

Author Collaborator

Should be better now, but we also shouldn't be commenting on a lone commit.

.OrderByDescending(s => s.CreateTimestamp)
.ToList()
.ElementAt(_settings.SubmissionRate.Submissions - 1)
.CreateTimestamp;
return _earliestTimestamp < DateTime.UtcNow.AddDays(-_settings.SubmissionRate.Days);
}
}
2 changes: 1 addition & 1 deletion TASVideos/appsettings.Production.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"Microsoft.AspNetCore.Identity": "Error",
"Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.AutoValidateAntiforgeryTokenAuthorizationFilter": "Information"
}
},
},
"WriteTo": [
{ "Name": "Console" },
{
Expand Down
Loading

0 comments on commit aeee02d

Please sign in to comment.