diff --git a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor index 3d7af836..120ede22 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor @@ -38,6 +38,7 @@ + @@ -96,82 +97,89 @@ @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) { diff --git a/Oqtane.Client/Modules/Admin/Jobs/Index.razor b/Oqtane.Client/Modules/Admin/Jobs/Index.razor index a71b6d68..ecc195e4 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Index.razor @@ -49,60 +49,62 @@ else } @code { - private List _jobs; + private List _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; } diff --git a/Oqtane.Client/Modules/Admin/Reset/Index.razor b/Oqtane.Client/Modules/Admin/Reset/Index.razor index 545a573d..6fd06013 100644 --- a/Oqtane.Client/Modules/Admin/Reset/Index.razor +++ b/Oqtane.Client/Modules/Admin/Reset/Index.razor @@ -19,9 +19,10 @@ - - +
+ + @code { diff --git a/Oqtane.Client/Modules/Controls/Pager.razor b/Oqtane.Client/Modules/Controls/Pager.razor index 40dc54ca..bd491d1f 100644 --- a/Oqtane.Client/Modules/Controls/Pager.razor +++ b/Oqtane.Client/Modules/Controls/Pager.razor @@ -107,8 +107,8 @@
  • UpdateList(1))>
  • - @if (_pages > _displayPages) - { + @if (_pages > _displayPages && _displayPages > 1) + {
  • SkipPages("back"))>
  • @@ -135,7 +135,7 @@
  • NavigateToPage("next"))>
  • - @if (_pages > _displayPages) + @if (_pages > _displayPages && _displayPages > 1) {
  • SkipPages("forward"))> @@ -145,7 +145,7 @@ UpdateList(_pages))>
  • - Page @_page of @_pages + Page @_page of @_pages
  • } diff --git a/Oqtane.Client/Resources/Modules/Admin/Jobs/Edit.resx b/Oqtane.Client/Resources/Modules/Admin/Jobs/Edit.resx index 569f2a71..facc630b 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Jobs/Edit.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Jobs/Edit.resx @@ -189,4 +189,7 @@ Week(s) + + Execute Once + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx index a5906e03..db97ddc3 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx @@ -189,4 +189,7 @@ Week(s) + + Execute Once + \ No newline at end of file diff --git a/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor b/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor index 60ef502a..960b2dd4 100644 --- a/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor @@ -210,7 +210,7 @@ - + @((MarkupString) Message) diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index b5dafc8b..34c2ab2a 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -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 diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 6e92bc15..89efac3e 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -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; } } diff --git a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs index 21cfff4f..16140a09 100644 --- a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs +++ b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs @@ -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) { diff --git a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css index 668d7fd3..7dbb674c 100644 --- a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css +++ b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css @@ -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 {