enhanced scheduler to support one-time jobs, fixed pager component so that top/bottom have consistent UX, fixed Blazor theme z-index issues caused by input-group in Bootstrap 5, improved password reset instructions in email notification

This commit is contained in:
Shaun Walker
2022-01-10 19:58:58 -05:00
parent efcfc0783c
commit 51b356cc0e
11 changed files with 167 additions and 139 deletions

View File

@ -38,6 +38,7 @@
<option value="d">@Localizer["Day(s)"]</option>
<option value="w">@Localizer["Week(s)"]</option>
<option value="M">@Localizer["Month(s)"]</option>
<option value="O">@Localizer["Once"]</option>
</select>
</div>
</div>
@ -171,7 +172,14 @@
job.JobType = _jobType;
job.IsEnabled = Boolean.Parse(_isEnabled);
job.Frequency = _frequency;
if (job.Frequency == "O") // once
{
job.Interval = 1;
}
else
{
job.Interval = int.Parse(_interval);
}
job.StartDate = _startDate;
if (job.StartDate != null)
{

View File

@ -83,26 +83,28 @@ else
private string DisplayFrequency(int interval, string frequency)
{
var result = $"{Localizer["Every"]} {interval.ToString()} ";
var result = "";
switch (frequency)
{
case "m":
result += Localizer["Minute"];
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Minute"];
break;
case "H":
result += Localizer["Hour"];
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Hour"];
break;
case "d":
result += Localizer["Day"];
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Day"];
break;
case "w":
result += Localizer["Week"];
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Week"];
break;
case "M":
result += Localizer["Month"];
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Month"];
break;
case "O":
result = Localizer["Once"];
break;
}
return result;
}

View File

@ -19,9 +19,10 @@
<label for="Confirm" class="control-label">@Localizer["Password.Confirm"] </label>
<input type="password" class="form-control" placeholder="Password" @bind="@_confirm" id="Confirm" required />
</div>
</div>
<br />
<button type="button" class="btn btn-primary" @onclick="Reset">@Localizer["Password.Reset"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
</div>
</form>
@code {

View File

@ -107,7 +107,7 @@
<li class="page-item@((_page > 1) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages)
@if (_pages > _displayPages && _displayPages > 1)
{
<li class="page-item@((_page > _displayPages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
@ -135,7 +135,7 @@
<li class="page-item@((_page < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages)
@if (_pages > _displayPages && _displayPages > 1)
{
<li class="page-item@((_endPage < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
@ -145,7 +145,7 @@
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
</li>
<li class="page-item disabled">
<a class="page-link">Page @_page of @_pages</a>
<a class="page-link" style="white-space: nowrap;">Page @_page of @_pages</a>
</li>
</ul>
}

View File

@ -189,4 +189,7 @@
<data name="Week(s)" xml:space="preserve">
<value>Week(s)</value>
</data>
<data name="Once" xml:space="preserve">
<value>Execute Once</value>
</data>
</root>

View File

@ -189,4 +189,7 @@
<data name="Week" xml:space="preserve">
<value>Week(s)</value>
</data>
<data name="Once" xml:space="preserve">
<value>Execute Once</value>
</data>
</root>

View File

@ -210,7 +210,7 @@
</select>
</div>
</div>
<button type="button" class="btn btn-success col-12 mt-4" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button>
<button type="button" class="btn btn-primary col-12 mt-4" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button>
@((MarkupString) Message)
</div>
</div>

View File

@ -19,7 +19,6 @@ using Oqtane.Extensions;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using System.Net.Http;
// ReSharper disable StringIndexOfIsCultureSpecific.1

View File

@ -389,7 +389,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid)
{
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
if (identityuser != null)
if (identityuser != null && !string.IsNullOrEmpty(token))
{
var result = await _identityUserManager.ConfirmEmailAsync(identityuser, token);
if (result.Succeeded)
@ -398,13 +398,13 @@ namespace Oqtane.Controllers
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}", user.Username);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username} - Error {Error}", user.Username, result.Errors.ToString());
user = null;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}", user.Username);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}And Token {Token}", user.Username, token);
user = null;
}
}
@ -420,9 +420,14 @@ namespace Oqtane.Controllers
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
if (identityuser != null)
{
user = _users.GetUser(user.Username);
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
string url = HttpContext.Request.Scheme + "://" + _alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
string body = "Dear " + user.DisplayName + ",\n\nPlease Click The Link Displayed Below To Reset Your Password:\n\n" + url + "\n\nThank You!";
string body = "Dear " + user.DisplayName + ",\n\nYou recently requested to reset your password. Please use the link below to complete the process:\n\n" + url +
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
"\n\nIf you did not request to reset your password you can safely ignore this message." +
"\n\nThank You!";
var notification = new Notification(user.SiteId, null, user, "User Password Reset", body, null);
_notifications.AddNotification(notification);
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username);
@ -451,13 +456,13 @@ namespace Oqtane.Controllers
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username}", user.Username);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username} - Error {Error}", user.Username, result.Errors.ToString());
user = null;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username}", user.Username);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username} And Token {Token}", user.Username, token);
user = null;
}
}

View File

@ -115,6 +115,11 @@ namespace Oqtane.Infrastructure
// update the job
job.NextExecution = CalculateNextExecution(NextExecution, job);
if (job.Frequency == "O") // one time
{
job.EndDate = DateTime.UtcNow;
job.NextExecution = null;
}
job.IsExecuting = false;
jobs.UpdateJob(job);
@ -174,6 +179,8 @@ namespace Oqtane.Infrastructure
nextExecution = nextExecution.Date.Add(job.StartDate.Value.TimeOfDay);
}
break;
case "O": // one time
break;
}
if (nextExecution < DateTime.UtcNow)
{

View File

@ -137,7 +137,7 @@
position: fixed;
left: 275px;
top: 15px;
z-index: 3
z-index: 6
}
.sidebar {
@ -145,13 +145,13 @@
height: 100vh;
position: sticky;
top: 0;
z-index: 1
z-index: 4
}
.main .top-row {
position: sticky;
top: 0;
z-index: 2
z-index: 5
}
.main > div {
@ -207,7 +207,7 @@
top: 150px;
width: 100%;
left: 0;
z-index: 1;
z-index: 4;
}
.sidebar {