Merge pull request #1925 from sbwalker/dev
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:
commit
66b13bdb8b
@ -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>
|
||||
@ -96,82 +97,89 @@
|
||||
</form>
|
||||
|
||||
@code {
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private int _jobId;
|
||||
private string _name = string.Empty;
|
||||
private string _jobType = string.Empty;
|
||||
private string _isEnabled = "True";
|
||||
private string _interval = string.Empty;
|
||||
private string _frequency = string.Empty;
|
||||
private DateTime? _startDate = null;
|
||||
private string _startTime = string.Empty;
|
||||
private DateTime? _endDate = null;
|
||||
private string _endTime = string.Empty;
|
||||
private string _retentionHistory = string.Empty;
|
||||
private DateTime? _nextDate = null;
|
||||
private string _nextTime = string.Empty;
|
||||
private string createdby;
|
||||
private DateTime createdon;
|
||||
private string modifiedby;
|
||||
private DateTime modifiedon;
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private int _jobId;
|
||||
private string _name = string.Empty;
|
||||
private string _jobType = string.Empty;
|
||||
private string _isEnabled = "True";
|
||||
private string _interval = string.Empty;
|
||||
private string _frequency = string.Empty;
|
||||
private DateTime? _startDate = null;
|
||||
private string _startTime = string.Empty;
|
||||
private DateTime? _endDate = null;
|
||||
private string _endTime = string.Empty;
|
||||
private string _retentionHistory = string.Empty;
|
||||
private DateTime? _nextDate = null;
|
||||
private string _nextTime = string.Empty;
|
||||
private string createdby;
|
||||
private DateTime createdon;
|
||||
private string modifiedby;
|
||||
private DateTime modifiedon;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_jobId = Int32.Parse(PageState.QueryString["id"]);
|
||||
Job job = await JobService.GetJobAsync(_jobId);
|
||||
if (job != null)
|
||||
{
|
||||
_name = job.Name;
|
||||
_jobType = job.JobType;
|
||||
_isEnabled = job.IsEnabled.ToString();
|
||||
_interval = job.Interval.ToString();
|
||||
_frequency = job.Frequency;
|
||||
_startDate = job.StartDate;
|
||||
if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
|
||||
{
|
||||
_startTime = job.StartDate.Value.ToString("HH:mm");
|
||||
}
|
||||
_endDate = job.EndDate;
|
||||
if (job.EndDate != null && job.EndDate.Value.TimeOfDay.TotalSeconds != 0)
|
||||
{
|
||||
_endTime = job.EndDate.Value.ToString("HH:mm");
|
||||
}
|
||||
_retentionHistory = job.RetentionHistory.ToString();
|
||||
_nextDate = job.NextExecution;
|
||||
if (job.NextExecution != null && job.NextExecution.Value.TimeOfDay.TotalSeconds != 0)
|
||||
{
|
||||
_nextTime = job.NextExecution.Value.ToString("HH:mm");
|
||||
}
|
||||
createdby = job.CreatedBy;
|
||||
createdon = job.CreatedOn;
|
||||
modifiedby = job.ModifiedBy;
|
||||
modifiedon = job.ModifiedOn;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Job {JobId} {Error}", _jobId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Job.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_jobId = Int32.Parse(PageState.QueryString["id"]);
|
||||
Job job = await JobService.GetJobAsync(_jobId);
|
||||
if (job != null)
|
||||
{
|
||||
_name = job.Name;
|
||||
_jobType = job.JobType;
|
||||
_isEnabled = job.IsEnabled.ToString();
|
||||
_interval = job.Interval.ToString();
|
||||
_frequency = job.Frequency;
|
||||
_startDate = job.StartDate;
|
||||
if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
|
||||
{
|
||||
_startTime = job.StartDate.Value.ToString("HH:mm");
|
||||
}
|
||||
_endDate = job.EndDate;
|
||||
if (job.EndDate != null && job.EndDate.Value.TimeOfDay.TotalSeconds != 0)
|
||||
{
|
||||
_endTime = job.EndDate.Value.ToString("HH:mm");
|
||||
}
|
||||
_retentionHistory = job.RetentionHistory.ToString();
|
||||
_nextDate = job.NextExecution;
|
||||
if (job.NextExecution != null && job.NextExecution.Value.TimeOfDay.TotalSeconds != 0)
|
||||
{
|
||||
_nextTime = job.NextExecution.Value.ToString("HH:mm");
|
||||
}
|
||||
createdby = job.CreatedBy;
|
||||
createdon = job.CreatedOn;
|
||||
modifiedby = job.ModifiedBy;
|
||||
modifiedon = job.ModifiedOn;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Job {JobId} {Error}", _jobId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Job.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveJob()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
var job = await JobService.GetJobAsync(_jobId);
|
||||
job.Name = _name;
|
||||
job.JobType = _jobType;
|
||||
job.IsEnabled = Boolean.Parse(_isEnabled);
|
||||
job.Frequency = _frequency;
|
||||
job.Interval = int.Parse(_interval);
|
||||
private async Task SaveJob()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
var job = await JobService.GetJobAsync(_jobId);
|
||||
job.Name = _name;
|
||||
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)
|
||||
{
|
||||
|
@ -49,60 +49,62 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Job> _jobs;
|
||||
private List<Job> _jobs;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
|
||||
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_jobs = await JobService.GetJobsAsync();
|
||||
}
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_jobs = await JobService.GetJobsAsync();
|
||||
}
|
||||
|
||||
private string DisplayStatus(bool isEnabled, bool isExecuting)
|
||||
{
|
||||
var status = string.Empty;
|
||||
if (!isEnabled)
|
||||
{
|
||||
status = Localizer["Disabled"];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isExecuting)
|
||||
{
|
||||
status = Localizer["Executing"];
|
||||
}
|
||||
else
|
||||
{
|
||||
status = Localizer["Idle"];
|
||||
}
|
||||
}
|
||||
private string DisplayStatus(bool isEnabled, bool isExecuting)
|
||||
{
|
||||
var status = string.Empty;
|
||||
if (!isEnabled)
|
||||
{
|
||||
status = Localizer["Disabled"];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isExecuting)
|
||||
{
|
||||
status = Localizer["Executing"];
|
||||
}
|
||||
else
|
||||
{
|
||||
status = Localizer["Idle"];
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
private string DisplayFrequency(int interval, string frequency)
|
||||
{
|
||||
var result = $"{Localizer["Every"]} {interval.ToString()} ";
|
||||
switch (frequency)
|
||||
{
|
||||
case "m":
|
||||
result += Localizer["Minute"];
|
||||
break;
|
||||
case "H":
|
||||
result += Localizer["Hour"];
|
||||
break;
|
||||
case "d":
|
||||
result += Localizer["Day"];
|
||||
break;
|
||||
case "w":
|
||||
result += Localizer["Week"];
|
||||
break;
|
||||
case "M":
|
||||
result += Localizer["Month"];
|
||||
break;
|
||||
}
|
||||
|
||||
private string DisplayFrequency(int interval, string frequency)
|
||||
{
|
||||
var result = "";
|
||||
switch (frequency)
|
||||
{
|
||||
case "m":
|
||||
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Minute"];
|
||||
break;
|
||||
case "H":
|
||||
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Hour"];
|
||||
break;
|
||||
case "d":
|
||||
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Day"];
|
||||
break;
|
||||
case "w":
|
||||
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Week"];
|
||||
break;
|
||||
case "M":
|
||||
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Month"];
|
||||
break;
|
||||
case "O":
|
||||
result = Localizer["Once"];
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
<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>
|
||||
<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>
|
||||
</form>
|
||||
|
||||
@code {
|
||||
|
@ -107,8 +107,8 @@
|
||||
<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>
|
||||
</li>
|
||||
@ -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>
|
||||
}
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user