diff --git a/Oqtane.Client/Modules/Admin/Jobs/Add.razor b/Oqtane.Client/Modules/Admin/Jobs/Add.razor
new file mode 100644
index 00000000..29c77c6a
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Jobs/Add.razor
@@ -0,0 +1,137 @@
+@namespace Oqtane.Modules.Admin.Jobs
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject IJobService JobService
+
+
+
+Cancel
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
+
+ string name = "";
+ string jobtype = "";
+ string isenabled = "True";
+ string interval = "";
+ string frequency = "";
+ string startdate = "";
+ string enddate = "";
+ string retentionhistory = "10";
+
+ private async Task SaveJob()
+ {
+ if (name != "" && !string.IsNullOrEmpty(jobtype) && frequency != "" && interval != "" && retentionhistory != "")
+ {
+ Job job = new Job();
+ job.Name = name;
+ job.JobType = jobtype;
+ job.IsEnabled = Boolean.Parse(isenabled);
+ job.Frequency = frequency;
+ job.Interval = int.Parse(interval);
+ if (startdate == "")
+ {
+ job.StartDate = null;
+ }
+ else
+ {
+ job.StartDate = DateTime.Parse(startdate);
+ }
+ if (enddate == "")
+ {
+ job.EndDate = null;
+ }
+ else
+ {
+ job.EndDate = DateTime.Parse(enddate);
+ }
+ job.RetentionHistory = int.Parse(retentionhistory);
+ job.IsStarted = false;
+ job.IsExecuting = false;
+ job.NextExecution = null;
+
+ try
+ {
+ job = await JobService.AddJobAsync(job);
+ await logger.LogInformation("Job Added {Job}", job);
+ NavigationManager.NavigateTo(NavigateUrl());
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Adding Job {Job} {Error}", job, ex.Message);
+ AddModuleMessage("Error Adding Job", MessageType.Error);
+ }
+ }
+ else
+ {
+ AddModuleMessage("You Must Provide The Job Name, Type, Frequency, and Retention", MessageType.Warning);
+ }
+ }
+
+}
diff --git a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor
new file mode 100644
index 00000000..a2651e87
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor
@@ -0,0 +1,160 @@
+@namespace Oqtane.Modules.Admin.Jobs
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject IJobService JobService
+
+
+
+Cancel
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
+
+ int jobid;
+ string name = "";
+ string jobtype = "";
+ string isenabled = "True";
+ string interval = "";
+ string frequency = "";
+ string startdate = "";
+ string enddate = "";
+ string retentionhistory = "";
+
+ 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 != null) ? job.StartDate.ToString() : "";
+ enddate = (job.EndDate != null) ? job.EndDate.ToString() : "";
+ retentionhistory = job.RetentionHistory.ToString();
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading Job {JobId} {Error}", jobid, ex.Message);
+ AddModuleMessage("Error Loading Job", MessageType.Error);
+ }
+ }
+
+ private async Task SaveJob()
+ {
+ if (name != "" && !string.IsNullOrEmpty(jobtype) && frequency != "" && interval != "" && retentionhistory != "")
+ {
+ Job job = await JobService.GetJobAsync(jobid);
+ job.Name = name;
+ job.JobType = jobtype;
+ job.IsEnabled = Boolean.Parse(isenabled);
+ job.Frequency = frequency;
+ job.Interval = int.Parse(interval);
+ if (startdate == "")
+ {
+ job.StartDate = null;
+ }
+ else
+ {
+ job.StartDate = DateTime.Parse(startdate);
+ }
+ if (enddate == "")
+ {
+ job.EndDate = null;
+ }
+ else
+ {
+ job.EndDate = DateTime.Parse(enddate);
+ }
+ job.RetentionHistory = int.Parse(retentionhistory);
+
+ try
+ {
+ job = await JobService.UpdateJobAsync(job);
+ await logger.LogInformation("Job Updated {Job}", job);
+ NavigationManager.NavigateTo(NavigateUrl());
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Udate Job {Job} {Error}", job, ex.Message);
+ AddModuleMessage("Error Updating Job", MessageType.Error);
+ }
+ }
+ else
+ {
+ AddModuleMessage("You Must Provide The Job Name, Type, Frequency, and Retention", MessageType.Warning);
+ }
+ }
+
+}
diff --git a/Oqtane.Client/Modules/Admin/Jobs/Index.razor b/Oqtane.Client/Modules/Admin/Jobs/Index.razor
new file mode 100644
index 00000000..e908225d
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Jobs/Index.razor
@@ -0,0 +1,137 @@
+@namespace Oqtane.Modules.Admin.Jobs
+@inherits ModuleBase
+@inject IJobService JobService
+
+@if (Jobs == null)
+{
+ Loading...
+}
+else
+{
+
+
+
+
+
+
+
+ |
+ |
+ |
+ Name |
+ Status |
+ Frequency |
+ Next Execution |
+ |
+
+
+ |
+ |
+ |
+ @context.Name |
+ @DisplayStatus(context.IsEnabled, context.IsExecuting) |
+ @DisplayFrequency(context.Interval, context.Frequency) |
+ @context.NextExecution |
+
+ @if (context.IsStarted)
+ {
+
+ }
+ else
+ {
+
+ }
+ |
+
+
+}
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
+
+ List Jobs;
+
+ protected override async Task OnParametersSetAsync()
+ {
+ Jobs = await JobService.GetJobsAsync();
+ }
+
+ private string DisplayStatus(bool IsEnabled, bool IsExecuting)
+ {
+ string status = "";
+ if (!IsEnabled)
+ {
+ status = "Disabled";
+ }
+ else
+ {
+ if (IsExecuting)
+ {
+ status = "Executing";
+ }
+ else
+ {
+ status = "Idle";
+ }
+ }
+
+ return status;
+ }
+
+
+ private string DisplayFrequency(int Interval, string Frequency)
+ {
+ string frequency = "Every " + Interval.ToString() + " ";
+ switch (Frequency)
+ {
+ case "m":
+ frequency += "Minute";
+ break;
+ case "H":
+ frequency += "Hour";
+ break;
+ case "d":
+ frequency += "Day";
+ break;
+ case "M":
+ frequency += "Month";
+ break;
+ }
+ if (Interval > 1)
+ {
+ frequency += "s";
+ }
+ return frequency;
+ }
+
+ private async Task DeleteJob(Job Job)
+ {
+ try
+ {
+ await JobService.DeleteJobAsync(Job.JobId);
+ await logger.LogInformation("Job Deleted {Job}", Job);
+ StateHasChanged();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Deleting Job {Job} {Error}", Job, ex.Message);
+ AddModuleMessage("Error Deleting Job", MessageType.Error);
+ }
+ }
+
+ private async Task StartJob(int JobId)
+ {
+ await JobService.StartJobAsync(JobId);
+ }
+
+ private async Task StopJob(int JobId)
+ {
+ await JobService.StopJobAsync(JobId);
+ }
+
+ private async Task Refresh()
+ {
+ Jobs = await JobService.GetJobsAsync();
+ StateHasChanged();
+ }
+}
\ No newline at end of file
diff --git a/Oqtane.Client/Modules/Admin/Jobs/Log.razor b/Oqtane.Client/Modules/Admin/Jobs/Log.razor
new file mode 100644
index 00000000..bc48f07c
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Jobs/Log.razor
@@ -0,0 +1,64 @@
+@namespace Oqtane.Modules.Admin.Jobs
+@inherits ModuleBase
+@inject IJobLogService JobLogService
+
+@if (JobLogs == null)
+{
+ Loading...
+}
+else
+{
+
+
+ Name |
+ Status |
+ Started |
+ Finished |
+ Notes |
+
+
+ @context.Job.Name |
+ @DisplayStatus(context.Job.IsExecuting, context.Succeeded) |
+ @context.StartDate |
+ @context.FinishDate |
+ |
+
+
+}
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
+
+ List JobLogs;
+
+ protected override async Task OnParametersSetAsync()
+ {
+ JobLogs = await JobLogService.GetJobLogsAsync();
+ if (PageState.QueryString.ContainsKey("id"))
+ {
+ JobLogs = JobLogs.Where(item => item.JobId == Int32.Parse(PageState.QueryString["id"])).ToList();
+ }
+ JobLogs = JobLogs.OrderByDescending(item => item.JobLogId).ToList();
+ }
+
+ private string DisplayStatus(bool IsExecuting, bool? Succeeded)
+ {
+ string status = "";
+ if (IsExecuting)
+ {
+ status = "Executing";
+ }
+ else
+ {
+ if (Succeeded.Value)
+ {
+ status = "Succeeded";
+ }
+ else
+ {
+ status = "Failed";
+ }
+ }
+ return status;
+ }
+}
\ No newline at end of file
diff --git a/Oqtane.Client/Modules/Admin/Roles/Add.razor b/Oqtane.Client/Modules/Admin/Roles/Add.razor
index f4e26aa8..c2bff1da 100644
--- a/Oqtane.Client/Modules/Admin/Roles/Add.razor
+++ b/Oqtane.Client/Modules/Admin/Roles/Add.razor
@@ -56,14 +56,15 @@
private async Task SaveRole()
{
+ Role role = new Role();
+ role.SiteId = PageState.Page.SiteId;
+ role.Name = name;
+ role.Description = description;
+ role.IsAutoAssigned = (isautoassigned == null ? false : Boolean.Parse(isautoassigned));
+ role.IsSystem = (issystem == null ? false : Boolean.Parse(issystem));
+
try
{
- Role role = new Role();
- role.SiteId = PageState.Page.SiteId;
- role.Name = name;
- role.Description = description;
- role.IsAutoAssigned = (isautoassigned == null ? false : Boolean.Parse(isautoassigned));
- role.IsSystem = (issystem == null ? false : Boolean.Parse(issystem));
role = await RoleService.AddRoleAsync(role);
await logger.LogInformation("Role Added {Role}", role);
@@ -71,8 +72,8 @@
}
catch (Exception ex)
{
- await logger.LogError(ex, "Error Adding Role", ex.Message);
- AddModuleMessage(ex.Message, MessageType.Error);
+ await logger.LogError(ex, "Error Adding Role {Role} {Error}", role, ex.Message);
+ AddModuleMessage("Error Adding Role", MessageType.Error);
}
}
diff --git a/Oqtane.Client/Modules/Admin/Roles/Edit.razor b/Oqtane.Client/Modules/Admin/Roles/Edit.razor
index 3ab20d91..2557bd44 100644
--- a/Oqtane.Client/Modules/Admin/Roles/Edit.razor
+++ b/Oqtane.Client/Modules/Admin/Roles/Edit.razor
@@ -78,20 +78,21 @@
private async Task SaveRole()
{
+ Role role = await RoleService.GetRoleAsync(roleid);
+ role.Name = name;
+ role.Description = description;
+ role.IsAutoAssigned = (isautoassigned == null ? false : Boolean.Parse(isautoassigned));
+ role.IsSystem = (issystem == null ? false : Boolean.Parse(issystem));
+
try
{
- Role role = await RoleService.GetRoleAsync(roleid);
- role.Name = name;
- role.Description = description;
- role.IsAutoAssigned = (isautoassigned == null ? false : Boolean.Parse(isautoassigned));
- role.IsSystem = (issystem == null ? false : Boolean.Parse(issystem));
role = await RoleService.UpdateRoleAsync(role);
await logger.LogInformation("Role Saved {Role}", role);
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{
- await logger.LogError(ex, "Error Saving Role {RoleId} {Error}", roleid, ex.Message);
+ await logger.LogError(ex, "Error Saving Role {Role} {Error}", role, ex.Message);
AddModuleMessage("Error Saving Role", MessageType.Error);
}
}
diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor
index 41dd4b21..96aac6c1 100644
--- a/Oqtane.Client/Modules/Controls/ActionDialog.razor
+++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor
@@ -15,7 +15,10 @@
@Message
@@ -36,29 +39,25 @@
public string Message { get; set; } // required
[Parameter]
- public string Action { get; set; } // defaults to Ok if not specified
+ public string Text { get; set; } // optional - defaults to Action if not specified
+
+ [Parameter]
+ public string Action { get; set; } // optional
[Parameter]
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
- [Parameter]
- public string Text { get; set; } // optional - defaults to Action if not specified
-
[Parameter]
public string Class { get; set; } // optional
[Parameter]
- public Action OnClick { get; set; } // required - executes a method in the calling component
+ public Action OnClick { get; set; } // required if an Action is specified - executes a method in the calling component
bool visible = false;
bool authorized = false;
protected override void OnParametersSet()
{
- if (string.IsNullOrEmpty(Action))
- {
- Action = "Ok";
- }
if (string.IsNullOrEmpty(Text))
{
Text = Action;
diff --git a/Oqtane.Client/Services/Interfaces/IJobLogService.cs b/Oqtane.Client/Services/Interfaces/IJobLogService.cs
new file mode 100644
index 00000000..2afc2851
--- /dev/null
+++ b/Oqtane.Client/Services/Interfaces/IJobLogService.cs
@@ -0,0 +1,19 @@
+using Oqtane.Models;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Oqtane.Services
+{
+ public interface IJobLogService
+ {
+ Task> GetJobLogsAsync();
+
+ Task GetJobLogAsync(int JobLogId);
+
+ Task AddJobLogAsync(JobLog JobLog);
+
+ Task UpdateJobLogAsync(JobLog JobLog);
+
+ Task DeleteJobLogAsync(int JobLogId);
+ }
+}
diff --git a/Oqtane.Client/Services/Interfaces/IJobService.cs b/Oqtane.Client/Services/Interfaces/IJobService.cs
new file mode 100644
index 00000000..15b508f9
--- /dev/null
+++ b/Oqtane.Client/Services/Interfaces/IJobService.cs
@@ -0,0 +1,23 @@
+using Oqtane.Models;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Oqtane.Services
+{
+ public interface IJobService
+ {
+ Task> GetJobsAsync();
+
+ Task GetJobAsync(int JobId);
+
+ Task AddJobAsync(Job Job);
+
+ Task UpdateJobAsync(Job Job);
+
+ Task DeleteJobAsync(int JobId);
+
+ Task StartJobAsync(int JobId);
+
+ Task StopJobAsync(int JobId);
+ }
+}
diff --git a/Oqtane.Client/Services/Interfaces/IScheduleLogService.cs b/Oqtane.Client/Services/Interfaces/IScheduleLogService.cs
deleted file mode 100644
index d3f8af27..00000000
--- a/Oqtane.Client/Services/Interfaces/IScheduleLogService.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Oqtane.Models;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace Oqtane.Services
-{
- public interface IScheduleLogService
- {
- Task> GetScheduleLogsAsync();
-
- Task GetScheduleLogAsync(int ScheduleLogId);
-
- Task AddScheduleLogAsync(ScheduleLog ScheduleLog);
-
- Task UpdateScheduleLogAsync(ScheduleLog ScheduleLog);
-
- Task DeleteScheduleLogAsync(int ScheduleLogId);
- }
-}
diff --git a/Oqtane.Client/Services/Interfaces/IScheduleService.cs b/Oqtane.Client/Services/Interfaces/IScheduleService.cs
deleted file mode 100644
index f9d7a00a..00000000
--- a/Oqtane.Client/Services/Interfaces/IScheduleService.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Oqtane.Models;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace Oqtane.Services
-{
- public interface IScheduleService
- {
- Task> GetSchedulesAsync();
-
- Task GetScheduleAsync(int ScheduleId);
-
- Task AddScheduleAsync(Schedule Schedule);
-
- Task UpdateScheduleAsync(Schedule Schedule);
-
- Task DeleteScheduleAsync(int ScheduleId);
- }
-}
diff --git a/Oqtane.Client/Services/JobLogService.cs b/Oqtane.Client/Services/JobLogService.cs
new file mode 100644
index 00000000..bd1d0c44
--- /dev/null
+++ b/Oqtane.Client/Services/JobLogService.cs
@@ -0,0 +1,54 @@
+using Oqtane.Models;
+using System.Threading.Tasks;
+using System.Net.Http;
+using System.Linq;
+using Microsoft.AspNetCore.Components;
+using System.Collections.Generic;
+using Oqtane.Shared;
+
+namespace Oqtane.Services
+{
+ public class JobLogService : ServiceBase, IJobLogService
+ {
+ private readonly HttpClient http;
+ private readonly SiteState sitestate;
+ private readonly NavigationManager NavigationManager;
+
+ public JobLogService(HttpClient http, SiteState sitestate, NavigationManager NavigationManager)
+ {
+ this.http = http;
+ this.sitestate = sitestate;
+ this.NavigationManager = NavigationManager;
+ }
+
+ private string apiurl
+ {
+ get { return CreateApiUrl(sitestate.Alias, NavigationManager.Uri, "JobLog"); }
+ }
+
+ public async Task> GetJobLogsAsync()
+ {
+ List Joblogs = await http.GetJsonAsync>(apiurl);
+ return Joblogs.OrderBy(item => item.StartDate).ToList();
+ }
+
+ public async Task GetJobLogAsync(int JobLogId)
+ {
+ return await http.GetJsonAsync(apiurl + "/" + JobLogId.ToString());
+ }
+
+ public async Task AddJobLogAsync(JobLog Joblog)
+ {
+ return await http.PostJsonAsync(apiurl, Joblog);
+ }
+
+ public async Task UpdateJobLogAsync(JobLog Joblog)
+ {
+ return await http.PutJsonAsync(apiurl + "/" + Joblog.JobLogId.ToString(), Joblog);
+ }
+ public async Task DeleteJobLogAsync(int JobLogId)
+ {
+ await http.DeleteAsync(apiurl + "/" + JobLogId.ToString());
+ }
+ }
+}
diff --git a/Oqtane.Client/Services/JobService.cs b/Oqtane.Client/Services/JobService.cs
new file mode 100644
index 00000000..8481b0b1
--- /dev/null
+++ b/Oqtane.Client/Services/JobService.cs
@@ -0,0 +1,64 @@
+using Oqtane.Models;
+using System.Threading.Tasks;
+using System.Net.Http;
+using System.Linq;
+using Microsoft.AspNetCore.Components;
+using System.Collections.Generic;
+using Oqtane.Shared;
+
+namespace Oqtane.Services
+{
+ public class JobService : ServiceBase, IJobService
+ {
+ private readonly HttpClient http;
+ private readonly SiteState sitestate;
+ private readonly NavigationManager NavigationManager;
+
+ public JobService(HttpClient http, SiteState sitestate, NavigationManager NavigationManager)
+ {
+ this.http = http;
+ this.sitestate = sitestate;
+ this.NavigationManager = NavigationManager;
+ }
+
+ private string apiurl
+ {
+ get { return CreateApiUrl(sitestate.Alias, NavigationManager.Uri, "Job"); }
+ }
+
+ public async Task> GetJobsAsync()
+ {
+ List Jobs = await http.GetJsonAsync>(apiurl);
+ return Jobs.OrderBy(item => item.Name).ToList();
+ }
+
+ public async Task GetJobAsync(int JobId)
+ {
+ return await http.GetJsonAsync(apiurl + "/" + JobId.ToString());
+ }
+
+ public async Task AddJobAsync(Job Job)
+ {
+ return await http.PostJsonAsync(apiurl, Job);
+ }
+
+ public async Task UpdateJobAsync(Job Job)
+ {
+ return await http.PutJsonAsync(apiurl + "/" + Job.JobId.ToString(), Job);
+ }
+ public async Task DeleteJobAsync(int JobId)
+ {
+ await http.DeleteAsync(apiurl + "/" + JobId.ToString());
+ }
+
+ public async Task StartJobAsync(int JobId)
+ {
+ await http.GetAsync(apiurl + "/start/" + JobId.ToString());
+ }
+
+ public async Task StopJobAsync(int JobId)
+ {
+ await http.GetAsync(apiurl + "/stop/" + JobId.ToString());
+ }
+ }
+}
diff --git a/Oqtane.Client/Services/ScheduleLogService.cs b/Oqtane.Client/Services/ScheduleLogService.cs
deleted file mode 100644
index 68b6b154..00000000
--- a/Oqtane.Client/Services/ScheduleLogService.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using Oqtane.Models;
-using System.Threading.Tasks;
-using System.Net.Http;
-using System.Linq;
-using Microsoft.AspNetCore.Components;
-using System.Collections.Generic;
-using Oqtane.Shared;
-
-namespace Oqtane.Services
-{
- public class ScheduleLogService : ServiceBase, IScheduleLogService
- {
- private readonly HttpClient http;
- private readonly SiteState sitestate;
- private readonly NavigationManager NavigationManager;
-
- public ScheduleLogService(HttpClient http, SiteState sitestate, NavigationManager NavigationManager)
- {
- this.http = http;
- this.sitestate = sitestate;
- this.NavigationManager = NavigationManager;
- }
-
- private string apiurl
- {
- get { return CreateApiUrl(sitestate.Alias, NavigationManager.Uri, "ScheduleLog"); }
- }
-
- public async Task> GetScheduleLogsAsync()
- {
- List schedulelogs = await http.GetJsonAsync>(apiurl);
- return schedulelogs.OrderBy(item => item.StartDate).ToList();
- }
-
- public async Task GetScheduleLogAsync(int ScheduleLogId)
- {
- return await http.GetJsonAsync(apiurl + "/" + ScheduleLogId.ToString());
- }
-
- public async Task AddScheduleLogAsync(ScheduleLog schedulelog)
- {
- return await http.PostJsonAsync(apiurl, schedulelog);
- }
-
- public async Task UpdateScheduleLogAsync(ScheduleLog schedulelog)
- {
- return await http.PutJsonAsync(apiurl + "/" + schedulelog.ScheduleLogId.ToString(), schedulelog);
- }
- public async Task DeleteScheduleLogAsync(int ScheduleLogId)
- {
- await http.DeleteAsync(apiurl + "/" + ScheduleLogId.ToString());
- }
- }
-}
diff --git a/Oqtane.Client/Services/ScheduleService.cs b/Oqtane.Client/Services/ScheduleService.cs
deleted file mode 100644
index 6375ba9b..00000000
--- a/Oqtane.Client/Services/ScheduleService.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using Oqtane.Models;
-using System.Threading.Tasks;
-using System.Net.Http;
-using System.Linq;
-using Microsoft.AspNetCore.Components;
-using System.Collections.Generic;
-using Oqtane.Shared;
-
-namespace Oqtane.Services
-{
- public class ScheduleService : ServiceBase, IScheduleService
- {
- private readonly HttpClient http;
- private readonly SiteState sitestate;
- private readonly NavigationManager NavigationManager;
-
- public ScheduleService(HttpClient http, SiteState sitestate, NavigationManager NavigationManager)
- {
- this.http = http;
- this.sitestate = sitestate;
- this.NavigationManager = NavigationManager;
- }
-
- private string apiurl
- {
- get { return CreateApiUrl(sitestate.Alias, NavigationManager.Uri, "Schedule"); }
- }
-
- public async Task> GetSchedulesAsync()
- {
- List schedules = await http.GetJsonAsync>(apiurl);
- return schedules.OrderBy(item => item.Name).ToList();
- }
-
- public async Task GetScheduleAsync(int ScheduleId)
- {
- return await http.GetJsonAsync(apiurl + "/" + ScheduleId.ToString());
- }
-
- public async Task AddScheduleAsync(Schedule schedule)
- {
- return await http.PostJsonAsync(apiurl, schedule);
- }
-
- public async Task UpdateScheduleAsync(Schedule schedule)
- {
- return await http.PutJsonAsync(apiurl + "/" + schedule.ScheduleId.ToString(), schedule);
- }
- public async Task DeleteScheduleAsync(int ScheduleId)
- {
- await http.DeleteAsync(apiurl + "/" + ScheduleId.ToString());
- }
- }
-}
diff --git a/Oqtane.Client/Startup.cs b/Oqtane.Client/Startup.cs
index b201a879..e4afc001 100644
--- a/Oqtane.Client/Startup.cs
+++ b/Oqtane.Client/Startup.cs
@@ -54,8 +54,8 @@ namespace Oqtane.Client
services.AddScoped();
services.AddScoped();
services.AddScoped();
- services.AddScoped();
- services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
// dynamically register module contexts and repository services
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
diff --git a/Oqtane.Server/Controllers/JobController.cs b/Oqtane.Server/Controllers/JobController.cs
new file mode 100644
index 00000000..4dade1d9
--- /dev/null
+++ b/Oqtane.Server/Controllers/JobController.cs
@@ -0,0 +1,105 @@
+using System.Collections.Generic;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Authorization;
+using Oqtane.Repository;
+using Oqtane.Models;
+using Oqtane.Shared;
+using Oqtane.Infrastructure;
+using System;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Oqtane.Controllers
+{
+ [Route("{site}/api/[controller]")]
+ public class JobController : Controller
+ {
+ private readonly IJobRepository Jobs;
+ private readonly ILogManager logger;
+ private readonly IServiceProvider ServiceProvider;
+
+ public JobController(IJobRepository Jobs, ILogManager logger, IServiceProvider ServiceProvider)
+ {
+ this.Jobs = Jobs;
+ this.logger = logger;
+ this.ServiceProvider = ServiceProvider;
+ }
+
+ // GET: api/
+ [HttpGet]
+ public IEnumerable Get()
+ {
+ return Jobs.GetJobs();
+ }
+
+ // GET api//5
+ [HttpGet("{id}")]
+ public Job Get(int id)
+ {
+ return Jobs.GetJob(id);
+ }
+
+ // POST api/
+ [HttpPost]
+ [Authorize(Roles = Constants.HostRole)]
+ public Job Post([FromBody] Job Job)
+ {
+ if (ModelState.IsValid)
+ {
+ Job = Jobs.AddJob(Job);
+ logger.Log(LogLevel.Information, this, LogFunction.Create, "Job Added {Job}", Job);
+ }
+ return Job;
+ }
+
+ // PUT api//5
+ [HttpPut("{id}")]
+ [Authorize(Roles = Constants.HostRole)]
+ public Job Put(int id, [FromBody] Job Job)
+ {
+ if (ModelState.IsValid)
+ {
+ Job = Jobs.UpdateJob(Job);
+ logger.Log(LogLevel.Information, this, LogFunction.Update, "Job Updated {Job}", Job);
+ }
+ return Job;
+ }
+
+ // DELETE api//5
+ [HttpDelete("{id}")]
+ [Authorize(Roles = Constants.HostRole)]
+ public void Delete(int id)
+ {
+ Jobs.DeleteJob(id);
+ logger.Log(LogLevel.Information, this, LogFunction.Delete, "Job Deleted {JobId}", id);
+ }
+
+ // GET api//start
+ [HttpGet("start/{id}")]
+ [Authorize(Roles = Constants.HostRole)]
+ public void Start(int id)
+ {
+ Job job = Jobs.GetJob(id);
+ Type jobtype = Type.GetType(job.JobType);
+ if (jobtype != null)
+ {
+ var jobobject = ActivatorUtilities.CreateInstance(ServiceProvider, jobtype);
+ ((IHostedService)jobobject).StartAsync(new System.Threading.CancellationToken());
+ }
+ }
+
+ // GET api//stop
+ [HttpGet("stop/{id}")]
+ [Authorize(Roles = Constants.HostRole)]
+ public void Stop(int id)
+ {
+ Job job = Jobs.GetJob(id);
+ Type jobtype = Type.GetType(job.JobType);
+ if (jobtype != null)
+ {
+ var jobobject = ActivatorUtilities.CreateInstance(ServiceProvider, jobtype);
+ ((IHostedService)jobobject).StopAsync(new System.Threading.CancellationToken());
+ }
+ }
+ }
+}
diff --git a/Oqtane.Server/Controllers/ScheduleController.cs b/Oqtane.Server/Controllers/JobLogController.cs
similarity index 58%
rename from Oqtane.Server/Controllers/ScheduleController.cs
rename to Oqtane.Server/Controllers/JobLogController.cs
index 4975266b..f514b519 100644
--- a/Oqtane.Server/Controllers/ScheduleController.cs
+++ b/Oqtane.Server/Controllers/JobLogController.cs
@@ -9,55 +9,57 @@ using Oqtane.Infrastructure;
namespace Oqtane.Controllers
{
[Route("{site}/api/[controller]")]
- public class ScheduleController : Controller
+ public class JobLogController : Controller
{
- private readonly IScheduleRepository Schedules;
+ private readonly IJobLogRepository JobLogs;
private readonly ILogManager logger;
- public ScheduleController(IScheduleRepository Schedules, ILogManager logger)
+ public JobLogController(IJobLogRepository JobLogs, ILogManager logger)
{
- this.Schedules = Schedules;
+ this.JobLogs = JobLogs;
this.logger = logger;
}
// GET: api/
[HttpGet]
- public IEnumerable Get()
+ [Authorize(Roles = Constants.HostRole)]
+ public IEnumerable Get()
{
- return Schedules.GetSchedules();
+ return JobLogs.GetJobLogs();
}
// GET api//5
[HttpGet("{id}")]
- public Schedule Get(int id)
+ [Authorize(Roles = Constants.HostRole)]
+ public JobLog Get(int id)
{
- return Schedules.GetSchedule(id);
+ return JobLogs.GetJobLog(id);
}
// POST api/
[HttpPost]
[Authorize(Roles = Constants.HostRole)]
- public Schedule Post([FromBody] Schedule Schedule)
+ public JobLog Post([FromBody] JobLog JobLog)
{
if (ModelState.IsValid)
{
- Schedule = Schedules.AddSchedule(Schedule);
- logger.Log(LogLevel.Information, this, LogFunction.Create, "Schedule Added {Schedule}", Schedule);
+ JobLog = JobLogs.AddJobLog(JobLog);
+ logger.Log(LogLevel.Information, this, LogFunction.Create, "Job Log Added {JobLog}", JobLog);
}
- return Schedule;
+ return JobLog;
}
// PUT api//5
[HttpPut("{id}")]
[Authorize(Roles = Constants.HostRole)]
- public Schedule Put(int id, [FromBody] Schedule Schedule)
+ public JobLog Put(int id, [FromBody] JobLog JobLog)
{
if (ModelState.IsValid)
{
- Schedule = Schedules.UpdateSchedule(Schedule);
- logger.Log(LogLevel.Information, this, LogFunction.Update, "Schedule Updated {Schedule}", Schedule);
+ JobLog = JobLogs.UpdateJobLog(JobLog);
+ logger.Log(LogLevel.Information, this, LogFunction.Update, "Job Log Updated {JobLog}", JobLog);
}
- return Schedule;
+ return JobLog;
}
// DELETE api//5
@@ -65,8 +67,8 @@ namespace Oqtane.Controllers
[Authorize(Roles = Constants.HostRole)]
public void Delete(int id)
{
- Schedules.DeleteSchedule(id);
- logger.Log(LogLevel.Information, this, LogFunction.Delete, "Schedule Deleted {ScheduleId}", id);
+ JobLogs.DeleteJobLog(id);
+ logger.Log(LogLevel.Information, this, LogFunction.Delete, "Job Log Deleted {JobLogId}", id);
}
}
}
diff --git a/Oqtane.Server/Controllers/ScheduleLogController.cs b/Oqtane.Server/Controllers/ScheduleLogController.cs
deleted file mode 100644
index cdbea209..00000000
--- a/Oqtane.Server/Controllers/ScheduleLogController.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System.Collections.Generic;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Authorization;
-using Oqtane.Repository;
-using Oqtane.Models;
-using Oqtane.Shared;
-using Oqtane.Infrastructure;
-
-namespace Oqtane.Controllers
-{
- [Route("{site}/api/[controller]")]
- public class ScheduleLogController : Controller
- {
- private readonly IScheduleLogRepository ScheduleLogs;
- private readonly ILogManager logger;
-
- public ScheduleLogController(IScheduleLogRepository ScheduleLogs, ILogManager logger)
- {
- this.ScheduleLogs = ScheduleLogs;
- this.logger = logger;
- }
-
- // GET: api/
- [HttpGet]
- [Authorize(Roles = Constants.HostRole)]
- public IEnumerable Get()
- {
- return ScheduleLogs.GetScheduleLogs();
- }
-
- // GET api//5
- [HttpGet("{id}")]
- [Authorize(Roles = Constants.HostRole)]
- public ScheduleLog Get(int id)
- {
- return ScheduleLogs.GetScheduleLog(id);
- }
-
- // POST api/
- [HttpPost]
- [Authorize(Roles = Constants.HostRole)]
- public ScheduleLog Post([FromBody] ScheduleLog ScheduleLog)
- {
- if (ModelState.IsValid)
- {
- ScheduleLog = ScheduleLogs.AddScheduleLog(ScheduleLog);
- logger.Log(LogLevel.Information, this, LogFunction.Create, "Schedule Log Added {ScheduleLog}", ScheduleLog);
- }
- return ScheduleLog;
- }
-
- // PUT api//5
- [HttpPut("{id}")]
- [Authorize(Roles = Constants.HostRole)]
- public ScheduleLog Put(int id, [FromBody] ScheduleLog ScheduleLog)
- {
- if (ModelState.IsValid)
- {
- ScheduleLog = ScheduleLogs.UpdateScheduleLog(ScheduleLog);
- logger.Log(LogLevel.Information, this, LogFunction.Update, "Schedule Log Updated {ScheduleLog}", ScheduleLog);
- }
- return ScheduleLog;
- }
-
- // DELETE api//5
- [HttpDelete("{id}")]
- [Authorize(Roles = Constants.HostRole)]
- public void Delete(int id)
- {
- ScheduleLogs.DeleteScheduleLog(id);
- logger.Log(LogLevel.Information, this, LogFunction.Delete, "Schedule Log Deleted {ScheduleLogId}", id);
- }
- }
-}
diff --git a/Oqtane.Server/Infrastructure/HostedServiceBase.cs b/Oqtane.Server/Infrastructure/HostedServiceBase.cs
deleted file mode 100644
index 09b91572..00000000
--- a/Oqtane.Server/Infrastructure/HostedServiceBase.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using Oqtane.Models;
-using Oqtane.Repository;
-using Oqtane.Shared;
-
-namespace Oqtane.Infrastructure
-{
- public abstract class HostedServiceBase : IHostedService, IDisposable
- {
- private Task ExecutingTask;
- private readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();
- private readonly IServiceScopeFactory ServiceScopeFactory;
-
- public HostedServiceBase(IServiceScopeFactory ServiceScopeFactory)
- {
- this.ServiceScopeFactory = ServiceScopeFactory;
- }
-
- // abstract method must be overridden by job
- public abstract void ExecuteJob(IServiceProvider provider);
-
-
- protected async Task ExecuteAsync(CancellationToken stoppingToken)
- {
- try
- {
- while (!stoppingToken.IsCancellationRequested)
- {
- // allows consumption of scoped services
- using (var scope = ServiceScopeFactory.CreateScope())
- {
- string JobType = Utilities.GetFullTypeName(this.GetType().AssemblyQualifiedName);
- IScheduleRepository ScheduleRepository = scope.ServiceProvider.GetRequiredService();
- List schedules = ScheduleRepository.GetSchedules().ToList();
- Schedule schedule = schedules.Where(item => item.JobType == JobType).FirstOrDefault();
- if (schedule != null && schedule.IsActive)
- {
- ExecuteJob(scope.ServiceProvider);
- }
- }
-
- await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
- }
- }
- catch
- {
- // can occur during the initial installation as there is no DBContext
- }
-
- }
-
- public Task StartAsync(CancellationToken cancellationToken)
- {
- ExecutingTask = ExecuteAsync(CancellationTokenSource.Token);
-
- if (ExecutingTask.IsCompleted)
- {
- return ExecutingTask;
- }
-
- return Task.CompletedTask;
- }
-
- public async Task StopAsync(CancellationToken CancellationToken)
- {
- if (ExecutingTask == null)
- {
- return;
- }
-
- try
- {
- CancellationTokenSource.Cancel();
- }
- finally
- {
- await Task.WhenAny(ExecutingTask, Task.Delay(Timeout.Infinite, CancellationToken));
- }
- }
-
- public void Dispose()
- {
- CancellationTokenSource.Cancel();
- }
- }
-}
diff --git a/Oqtane.Server/Infrastructure/IInstallationManager.cs b/Oqtane.Server/Infrastructure/Interfaces/IInstallationManager.cs
similarity index 100%
rename from Oqtane.Server/Infrastructure/IInstallationManager.cs
rename to Oqtane.Server/Infrastructure/Interfaces/IInstallationManager.cs
diff --git a/Oqtane.Server/Infrastructure/ILogManager.cs b/Oqtane.Server/Infrastructure/Interfaces/ILogManager.cs
similarity index 100%
rename from Oqtane.Server/Infrastructure/ILogManager.cs
rename to Oqtane.Server/Infrastructure/Interfaces/ILogManager.cs
diff --git a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs
new file mode 100644
index 00000000..7b81a102
--- /dev/null
+++ b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs
@@ -0,0 +1,197 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Oqtane.Models;
+using Oqtane.Repository;
+using Oqtane.Shared;
+
+namespace Oqtane.Infrastructure
+{
+ public abstract class HostedServiceBase : IHostedService, IDisposable
+ {
+ private Task ExecutingTask;
+ private readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();
+ private readonly IServiceScopeFactory ServiceScopeFactory;
+
+ public HostedServiceBase(IServiceScopeFactory ServiceScopeFactory)
+ {
+ this.ServiceScopeFactory = ServiceScopeFactory;
+ }
+
+ // abstract method must be overridden
+ public abstract string ExecuteJob(IServiceProvider provider);
+
+ protected async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ try
+ {
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ // get name of job
+ string JobType = Utilities.GetFullTypeName(this.GetType().AssemblyQualifiedName);
+
+ // load jobs and find current job
+ IJobRepository Jobs = scope.ServiceProvider.GetRequiredService();
+ Job Job = Jobs.GetJobs().Where(item => item.JobType == JobType).FirstOrDefault();
+ if (Job != null && Job.IsEnabled && !Job.IsExecuting)
+ {
+ // set next execution date
+ if (Job.NextExecution == null)
+ {
+ if (Job.StartDate != null)
+ {
+ Job.NextExecution = Job.StartDate;
+ }
+ else
+ {
+ Job.NextExecution = DateTime.Now;
+ }
+ }
+
+ // determine if the job should be run
+ if (Job.NextExecution <= DateTime.Now && (Job.EndDate == null || Job.EndDate >= DateTime.Now))
+ {
+ IJobLogRepository JobLogs = scope.ServiceProvider.GetRequiredService();
+
+ // create a job log entry
+ JobLog log = new JobLog();
+ log.JobId = Job.JobId;
+ log.StartDate = DateTime.Now;
+ log.FinishDate = null;
+ log.Succeeded = false;
+ log.Notes = "";
+ log = JobLogs.AddJobLog(log);
+
+ // update the job to indicate it is running
+ Job.IsExecuting = true;
+ Jobs.UpdateJob(Job);
+
+ // execute the job
+ try
+ {
+ log.Notes = ExecuteJob(scope.ServiceProvider);
+ log.Succeeded = true;
+ }
+ catch (Exception ex)
+ {
+ log.Notes = ex.Message;
+ log.Succeeded = false;
+ }
+
+ // update the job log
+ log.FinishDate = DateTime.Now;
+ JobLogs.UpdateJobLog(log);
+
+ // update the job
+ Job.NextExecution = CalculateNextExecution(Job.NextExecution.Value, Job.Frequency, Job.Interval);
+ Job.IsExecuting = false;
+ Jobs.UpdateJob(Job);
+
+ // trim the job log
+ List logs = JobLogs.GetJobLogs().Where(item => item.JobId == Job.JobId)
+ .OrderByDescending(item => item.JobLogId).ToList();
+ for (int i = logs.Count; i > Job.RetentionHistory; i--)
+ {
+ JobLogs.DeleteJobLog(logs[i - 1].JobLogId);
+ }
+ }
+ }
+ }
+
+ // wait 1 minute
+ await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
+ }
+ }
+ catch
+ {
+ // can occur during the initial installation as there is no DBContext
+ }
+
+ }
+
+ private DateTime CalculateNextExecution(DateTime NextExecution, string Frequency, int Interval)
+ {
+ switch (Frequency)
+ {
+ case "m": // minutes
+ NextExecution = NextExecution.AddMinutes(Interval);
+ break;
+ case "H": // hours
+ NextExecution = NextExecution.AddHours(Interval);
+ break;
+ case "d": // days
+ NextExecution = NextExecution.AddDays(Interval);
+ break;
+ case "M": // months
+ NextExecution = NextExecution.AddMonths(Interval);
+ break;
+ }
+ if (NextExecution < DateTime.Now)
+ {
+ NextExecution = DateTime.Now;
+ }
+ return NextExecution;
+ }
+
+ public Task StartAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ // set IsExecuting to false in case this job was forcefully terminated previously
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ string JobType = Utilities.GetFullTypeName(this.GetType().AssemblyQualifiedName);
+ IJobRepository Jobs = scope.ServiceProvider.GetRequiredService();
+ Job Job = Jobs.GetJobs().Where(item => item.JobType == JobType).FirstOrDefault();
+ if (Job != null)
+ {
+ Job.IsStarted = true;
+ Job.IsExecuting = false;
+ Jobs.UpdateJob(Job);
+ }
+ }
+ }
+ catch
+ {
+ // can occur during the initial installation as there is no DBContext
+ }
+
+ ExecutingTask = ExecuteAsync(CancellationTokenSource.Token);
+
+ if (ExecutingTask.IsCompleted)
+ {
+ return ExecutingTask;
+ }
+
+ return Task.CompletedTask;
+ }
+
+ public async Task StopAsync(CancellationToken CancellationToken)
+ {
+ if (ExecutingTask == null)
+ {
+ return;
+ }
+
+ try
+ {
+ CancellationTokenSource.Cancel();
+ }
+ finally
+ {
+ await Task.WhenAny(ExecutingTask, Task.Delay(Timeout.Infinite, CancellationToken));
+ }
+ }
+
+ public void Dispose()
+ {
+ CancellationTokenSource.Cancel();
+ }
+ }
+}
diff --git a/Oqtane.Server/Infrastructure/Jobs/SampleJob.cs b/Oqtane.Server/Infrastructure/Jobs/SampleJob.cs
new file mode 100644
index 00000000..816fbaef
--- /dev/null
+++ b/Oqtane.Server/Infrastructure/Jobs/SampleJob.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Linq;
+using Microsoft.Extensions.DependencyInjection;
+using Oqtane.Models;
+using Oqtane.Repository;
+using Oqtane.Shared;
+
+namespace Oqtane.Infrastructure
+{
+ public class SampleJob : HostedServiceBase
+ {
+ // JobType = "Oqtane.Infrastructure.SampleJob, Oqtane.Server"
+
+ public SampleJob(IServiceScopeFactory ServiceScopeFactory) : base(ServiceScopeFactory) {}
+
+ public override string ExecuteJob(IServiceProvider provider)
+ {
+ // get the first alias for this installation
+ var Aliases = provider.GetRequiredService();
+ Alias alias = Aliases.GetAliases().FirstOrDefault();
+
+ // use the SiteState to set the Alias explicitly so the tenant can be resolved
+ var sitestate = provider.GetRequiredService();
+ sitestate.Alias = alias;
+
+ // call a repository service which requires tenant resolution
+ var Sites = provider.GetRequiredService();
+ Site site = Sites.GetSites().FirstOrDefault();
+
+ return "You Should Include Any Notes Related To The Execution Of The Schedule Job. This Job Simply Reports That The Default Site Is " + site.Name;
+ }
+ }
+}
diff --git a/Oqtane.Server/Infrastructure/TestJob.cs b/Oqtane.Server/Infrastructure/TestJob.cs
deleted file mode 100644
index 1cabfc2d..00000000
--- a/Oqtane.Server/Infrastructure/TestJob.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using Microsoft.Extensions.DependencyInjection;
-using Oqtane.Models;
-using Oqtane.Repository;
-
-namespace Oqtane.Infrastructure
-{
- public class TestJob : HostedServiceBase
- {
- public TestJob(IServiceScopeFactory ServiceScopeFactory) : base(ServiceScopeFactory) {}
-
- public override void ExecuteJob(IServiceProvider provider)
- {
- var Tenants = provider.GetRequiredService();
- foreach(Tenant tenant in Tenants.GetTenants())
- {
- // is it possible to set the HttpContext so that DbContextBase will resolve properly for tenants?
- }
- }
- }
-}
diff --git a/Oqtane.Server/Repository/Context/DBContextBase.cs b/Oqtane.Server/Repository/Context/DBContextBase.cs
index e6d0081b..bb1b2635 100644
--- a/Oqtane.Server/Repository/Context/DBContextBase.cs
+++ b/Oqtane.Server/Repository/Context/DBContextBase.cs
@@ -42,7 +42,7 @@ namespace Oqtane.Repository
ChangeTracker.DetectChanges();
string username = "";
- if (accessor.HttpContext.User.Identity.Name != null)
+ if (accessor.HttpContext != null && accessor.HttpContext.User.Identity.Name != null)
{
username = accessor.HttpContext.User.Identity.Name;
}
diff --git a/Oqtane.Server/Repository/Context/MasterDBContext.cs b/Oqtane.Server/Repository/Context/MasterDBContext.cs
index 3e0c2b8f..88663bfa 100644
--- a/Oqtane.Server/Repository/Context/MasterDBContext.cs
+++ b/Oqtane.Server/Repository/Context/MasterDBContext.cs
@@ -18,15 +18,15 @@ namespace Oqtane.Repository
public virtual DbSet Alias { get; set; }
public virtual DbSet Tenant { get; set; }
public virtual DbSet ModuleDefinition { get; set; }
- public virtual DbSet Schedule { get; set; }
- public virtual DbSet ScheduleLog { get; set; }
+ public virtual DbSet Job { get; set; }
+ public virtual DbSet JobLog { get; set; }
public override int SaveChanges()
{
ChangeTracker.DetectChanges();
string username = "";
- if (accessor.HttpContext.User.Identity.Name != null)
+ if (accessor.HttpContext != null && accessor.HttpContext.User.Identity.Name != null)
{
username = accessor.HttpContext.User.Identity.Name;
}
diff --git a/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs b/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs
new file mode 100644
index 00000000..f99de0d2
--- /dev/null
+++ b/Oqtane.Server/Repository/Interfaces/IJobLogRepository.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+using Oqtane.Models;
+
+namespace Oqtane.Repository
+{
+ public interface IJobLogRepository
+ {
+ IEnumerable GetJobLogs();
+ JobLog AddJobLog(JobLog JobLog);
+ JobLog UpdateJobLog(JobLog JobLog);
+ JobLog GetJobLog(int JobLogId);
+ void DeleteJobLog(int JobLogId);
+ }
+}
diff --git a/Oqtane.Server/Repository/Interfaces/IJobRepository.cs b/Oqtane.Server/Repository/Interfaces/IJobRepository.cs
new file mode 100644
index 00000000..8683009f
--- /dev/null
+++ b/Oqtane.Server/Repository/Interfaces/IJobRepository.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+using Oqtane.Models;
+
+namespace Oqtane.Repository
+{
+ public interface IJobRepository
+ {
+ IEnumerable GetJobs();
+ Job AddJob(Job Job);
+ Job UpdateJob(Job Job);
+ Job GetJob(int JobId);
+ void DeleteJob(int JobId);
+ }
+}
diff --git a/Oqtane.Server/Repository/Interfaces/IScheduleLogRepository.cs b/Oqtane.Server/Repository/Interfaces/IScheduleLogRepository.cs
deleted file mode 100644
index 240f1fee..00000000
--- a/Oqtane.Server/Repository/Interfaces/IScheduleLogRepository.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.Collections.Generic;
-using Oqtane.Models;
-
-namespace Oqtane.Repository
-{
- public interface IScheduleLogRepository
- {
- IEnumerable GetScheduleLogs();
- ScheduleLog AddScheduleLog(ScheduleLog ScheduleLog);
- ScheduleLog UpdateScheduleLog(ScheduleLog ScheduleLog);
- ScheduleLog GetScheduleLog(int ScheduleLogId);
- void DeleteScheduleLog(int ScheduleLogId);
- }
-}
diff --git a/Oqtane.Server/Repository/Interfaces/IScheduleRepository.cs b/Oqtane.Server/Repository/Interfaces/IScheduleRepository.cs
deleted file mode 100644
index 09c3b3ca..00000000
--- a/Oqtane.Server/Repository/Interfaces/IScheduleRepository.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.Collections.Generic;
-using Oqtane.Models;
-
-namespace Oqtane.Repository
-{
- public interface IScheduleRepository
- {
- IEnumerable GetSchedules();
- Schedule AddSchedule(Schedule Schedule);
- Schedule UpdateSchedule(Schedule Schedule);
- Schedule GetSchedule(int ScheduleId);
- void DeleteSchedule(int ScheduleId);
- }
-}
diff --git a/Oqtane.Server/Repository/JobLogRepository.cs b/Oqtane.Server/Repository/JobLogRepository.cs
new file mode 100644
index 00000000..66ef6f2e
--- /dev/null
+++ b/Oqtane.Server/Repository/JobLogRepository.cs
@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using System.Linq;
+using Oqtane.Models;
+using Microsoft.EntityFrameworkCore;
+
+namespace Oqtane.Repository
+{
+ public class JobLogRepository : IJobLogRepository
+ {
+ private MasterDBContext db;
+
+ public JobLogRepository(MasterDBContext context)
+ {
+ db = context;
+ }
+
+ public IEnumerable GetJobLogs()
+ {
+ return db.JobLog
+ .Include(item => item.Job) // eager load jobs
+ .ToList();
+ }
+
+ public JobLog AddJobLog(JobLog JobLog)
+ {
+ db.JobLog.Add(JobLog);
+ db.SaveChanges();
+ return JobLog;
+ }
+
+ public JobLog UpdateJobLog(JobLog JobLog)
+ {
+ db.Entry(JobLog).State = EntityState.Modified;
+ db.SaveChanges();
+ return JobLog;
+ }
+
+ public JobLog GetJobLog(int JobLogId)
+ {
+ return db.JobLog.Include(item => item.Job) // eager load job
+ .SingleOrDefault(item => item.JobLogId == JobLogId);
+ }
+
+ public void DeleteJobLog(int JobLogId)
+ {
+ JobLog Joblog = db.JobLog.Find(JobLogId);
+ db.JobLog.Remove(Joblog);
+ db.SaveChanges();
+ }
+ }
+}
diff --git a/Oqtane.Server/Repository/JobRepository.cs b/Oqtane.Server/Repository/JobRepository.cs
new file mode 100644
index 00000000..46ffaba4
--- /dev/null
+++ b/Oqtane.Server/Repository/JobRepository.cs
@@ -0,0 +1,49 @@
+using System.Collections.Generic;
+using System.Linq;
+using Oqtane.Models;
+using Microsoft.EntityFrameworkCore;
+using System;
+
+namespace Oqtane.Repository
+{
+ public class JobRepository : IJobRepository
+ {
+ private MasterDBContext db;
+
+ public JobRepository(MasterDBContext context)
+ {
+ db = context;
+ }
+
+ public IEnumerable GetJobs()
+ {
+ return db.Job.ToList();
+ }
+
+ public Job AddJob(Job Job)
+ {
+ db.Job.Add(Job);
+ db.SaveChanges();
+ return Job;
+ }
+
+ public Job UpdateJob(Job Job)
+ {
+ db.Entry(Job).State = EntityState.Modified;
+ db.SaveChanges();
+ return Job;
+ }
+
+ public Job GetJob(int JobId)
+ {
+ return db.Job.Find(JobId);
+ }
+
+ public void DeleteJob(int JobId)
+ {
+ Job Job = db.Job.Find(JobId);
+ db.Job.Remove(Job);
+ db.SaveChanges();
+ }
+ }
+}
diff --git a/Oqtane.Server/Repository/ScheduleLogRepository.cs b/Oqtane.Server/Repository/ScheduleLogRepository.cs
deleted file mode 100644
index 03b67295..00000000
--- a/Oqtane.Server/Repository/ScheduleLogRepository.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using Oqtane.Models;
-using Microsoft.EntityFrameworkCore;
-using System;
-
-namespace Oqtane.Repository
-{
- public class ScheduleLogRepository : IScheduleLogRepository
- {
- private MasterDBContext db;
-
- public ScheduleLogRepository(MasterDBContext context)
- {
- db = context;
- }
-
- public IEnumerable GetScheduleLogs()
- {
- return db.ScheduleLog.ToList();
- }
-
- public ScheduleLog AddScheduleLog(ScheduleLog ScheduleLog)
- {
- db.ScheduleLog.Add(ScheduleLog);
- db.SaveChanges();
- return ScheduleLog;
- }
-
- public ScheduleLog UpdateScheduleLog(ScheduleLog ScheduleLog)
- {
- db.Entry(ScheduleLog).State = EntityState.Modified;
- db.SaveChanges();
- return ScheduleLog;
- }
-
- public ScheduleLog GetScheduleLog(int ScheduleLogId)
- {
- return db.ScheduleLog.Find(ScheduleLogId);
- }
-
- public void DeleteScheduleLog(int ScheduleLogId)
- {
- ScheduleLog schedulelog = db.ScheduleLog.Find(ScheduleLogId);
- db.ScheduleLog.Remove(schedulelog);
- db.SaveChanges();
- }
- }
-}
diff --git a/Oqtane.Server/Repository/ScheduleRepository.cs b/Oqtane.Server/Repository/ScheduleRepository.cs
deleted file mode 100644
index 052d2467..00000000
--- a/Oqtane.Server/Repository/ScheduleRepository.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using Oqtane.Models;
-using Microsoft.EntityFrameworkCore;
-using System;
-
-namespace Oqtane.Repository
-{
- public class ScheduleRepository : IScheduleRepository
- {
- private MasterDBContext db;
-
- public ScheduleRepository(MasterDBContext context)
- {
- db = context;
- }
-
- public IEnumerable GetSchedules()
- {
- return db.Schedule.ToList();
- }
-
- public Schedule AddSchedule(Schedule Schedule)
- {
- db.Schedule.Add(Schedule);
- db.SaveChanges();
- return Schedule;
- }
-
- public Schedule UpdateSchedule(Schedule Schedule)
- {
- db.Entry(Schedule).State = EntityState.Modified;
- db.SaveChanges();
- return Schedule;
- }
-
- public Schedule GetSchedule(int ScheduleId)
- {
- return db.Schedule.Find(ScheduleId);
- }
-
- public void DeleteSchedule(int ScheduleId)
- {
- Schedule schedule = db.Schedule.Find(ScheduleId);
- db.Schedule.Remove(schedule);
- db.SaveChanges();
- }
- }
-}
diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs
index d21a7fe7..59a7233f 100644
--- a/Oqtane.Server/Repository/SiteRepository.cs
+++ b/Oqtane.Server/Repository/SiteRepository.cs
@@ -92,6 +92,9 @@ namespace Oqtane.Repository
SiteTemplate.Add(new PageTemplate { Name = "Theme Management", Parent = "Admin", Path = "admin/themes", Icon = "brush", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Themes, Oqtane.Client", Title = "Theme Management", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" }
}});
+ SiteTemplate.Add(new PageTemplate { Name = "Scheduled Jobs", Parent = "Admin", Path = "admin/jobs", Icon = "timer", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List {
+ new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Jobs, Oqtane.Client", Title = "Scheduled Jobs", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" }
+ }});
SiteTemplate.Add(new PageTemplate { Name = "Upgrade Service", Parent = "Admin", Path = "admin/upgrade", Icon = "aperture", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Upgrade, Oqtane.Client", Title = "Upgrade Service", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" }
}});
diff --git a/Oqtane.Server/Repository/TenantResolver.cs b/Oqtane.Server/Repository/TenantResolver.cs
index 30eb7c19..299a8a41 100644
--- a/Oqtane.Server/Repository/TenantResolver.cs
+++ b/Oqtane.Server/Repository/TenantResolver.cs
@@ -3,6 +3,7 @@ using System.Linq;
using Oqtane.Models;
using Microsoft.AspNetCore.Http;
using System;
+using Oqtane.Shared;
namespace Oqtane.Repository
{
@@ -12,12 +13,14 @@ namespace Oqtane.Repository
private readonly string aliasname;
private readonly IAliasRepository Aliases;
private readonly ITenantRepository Tenants;
+ private readonly SiteState sitestate;
- public TenantResolver(MasterDBContext context, IHttpContextAccessor accessor, IAliasRepository Aliases, ITenantRepository Tenants)
+ public TenantResolver(MasterDBContext context, IHttpContextAccessor accessor, IAliasRepository Aliases, ITenantRepository Tenants, SiteState sitestate)
{
db = context;
this.Aliases = Aliases;
this.Tenants = Tenants;
+ this.sitestate = sitestate;
aliasname = "";
// get alias based on request context
@@ -35,6 +38,13 @@ namespace Oqtane.Repository
aliasname = aliasname.Substring(0, aliasname.Length - 1);
}
}
+ else // background processes can pass in an alias using the SiteState service
+ {
+ if (sitestate != null)
+ {
+ aliasname = sitestate.Alias.Name;
+ }
+ }
}
public Alias GetAlias()
diff --git a/Oqtane.Server/Scripts/Master.sql b/Oqtane.Server/Scripts/Master.sql
index 2d68947a..b2f05b65 100644
--- a/Oqtane.Server/Scripts/Master.sql
+++ b/Oqtane.Server/Scripts/Master.sql
@@ -3,22 +3,6 @@
Create tables
*/
-CREATE TABLE [dbo].[Alias](
- [AliasId] [int] IDENTITY(1,1) NOT NULL,
- [Name] [nvarchar](200) NOT NULL,
- [TenantId] [int] NOT NULL,
- [SiteId] [int] NOT NULL,
- [CreatedBy] [nvarchar](256) NOT NULL,
- [CreatedOn] [datetime] NOT NULL,
- [ModifiedBy] [nvarchar](256) NOT NULL,
- [ModifiedOn] [datetime] NOT NULL,
- CONSTRAINT [PK_Alias] PRIMARY KEY CLUSTERED
- (
- [AliasId] ASC
- )
-)
-GO
-
CREATE TABLE [dbo].[Tenant](
[TenantId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NOT NULL,
@@ -36,6 +20,23 @@ CREATE TABLE [dbo].[Tenant](
)
GO
+CREATE TABLE [dbo].[Alias](
+ [AliasId] [int] IDENTITY(1,1) NOT NULL,
+ [Name] [nvarchar](200) NOT NULL,
+ [TenantId] [int] NOT NULL,
+ [SiteId] [int] NOT NULL,
+ [CreatedBy] [nvarchar](256) NOT NULL,
+ [CreatedOn] [datetime] NOT NULL,
+ [ModifiedBy] [nvarchar](256) NOT NULL,
+ [ModifiedOn] [datetime] NOT NULL,
+ CONSTRAINT [PK_Alias] PRIMARY KEY CLUSTERED
+ (
+ [AliasId] ASC
+ )
+)
+GO
+
+
CREATE TABLE [dbo].[ModuleDefinition](
[ModuleDefinitionId] [int] IDENTITY(1,1) NOT NULL,
[ModuleDefinitionName] [nvarchar](200) NOT NULL,
@@ -50,38 +51,40 @@ CREATE TABLE [dbo].[ModuleDefinition](
)
GO
-CREATE TABLE [dbo].[Schedule] (
- [ScheduleId] [int] IDENTITY(1,1) NOT NULL,
+CREATE TABLE [dbo].[Job] (
+ [JobId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](200) NOT NULL,
[JobType] [nvarchar](200) NOT NULL,
- [Period] [int] NOT NULL,
[Frequency] [char](1) NOT NULL,
+ [Interval] [int] NOT NULL,
[StartDate] [datetime] NULL,
- [IsActive] [bit] NOT NULL,
+ [EndDate] [datetime] NULL,
+ [IsEnabled] [bit] NOT NULL,
+ [IsStarted] [bit] NOT NULL,
[IsExecuting] [bit] NOT NULL,
[NextExecution] [datetime] NULL,
[RetentionHistory] [int] NOT NULL,
- [CreatedBy] [nvarchar](256) NULL,
- [CreatedOn] [datetime] NULL,
- [ModifiedBy] [nvarchar](256) NULL,
- [ModifiedOn] [datetime] NULL,
- CONSTRAINT [PK_Schedule] PRIMARY KEY CLUSTERED
+ [CreatedBy] [nvarchar](256) NOT NULL,
+ [CreatedOn] [datetime] NOT NULL,
+ [ModifiedBy] [nvarchar](256) NOT NULL,
+ [ModifiedOn] [datetime] NOT NULL,
+ CONSTRAINT [PK_Job] PRIMARY KEY CLUSTERED
(
- [ScheduleId] ASC
+ [JobId] ASC
)
)
GO
-CREATE TABLE [dbo].[ScheduleLog] (
- [ScheduleLogId] [int] IDENTITY(1,1) NOT NULL,
- [ScheduleId] [int] NOT NULL,
+CREATE TABLE [dbo].[JobLog] (
+ [JobLogId] [int] IDENTITY(1,1) NOT NULL,
+ [JobId] [int] NOT NULL,
[StartDate] [datetime] NOT NULL,
[FinishDate] [datetime] NULL,
[Succeeded] [bit] NULL,
[Notes] [nvarchar](max) NULL,
- CONSTRAINT [PK_ScheduleLog] PRIMARY KEY CLUSTERED
+ CONSTRAINT [PK_JobLog] PRIMARY KEY CLUSTERED
(
- [ScheduleLogId] ASC
+ [JobLogId] ASC
)
)
GO
@@ -107,8 +110,8 @@ REFERENCES [dbo].[Tenant] ([TenantId])
ON DELETE CASCADE
GO
-ALTER TABLE [dbo].[ScheduleLog] WITH NOCHECK ADD CONSTRAINT [FK_ScheduleLog_Schedule] FOREIGN KEY([ScheduleId])
-REFERENCES [dbo].[Schedule] ([ScheduleId])
+ALTER TABLE [dbo].[JobLog] WITH NOCHECK ADD CONSTRAINT [FK_JobLog_Job] FOREIGN KEY([JobId])
+REFERENCES [dbo].[Job] ([JobId])
ON DELETE CASCADE
GO
@@ -132,3 +135,12 @@ VALUES (1, N'{Alias}', 1, 1, '', getdate(), '', getdate())
GO
SET IDENTITY_INSERT [dbo].[Alias] OFF
GO
+
+SET IDENTITY_INSERT [dbo].[Job] ON
+GO
+INSERT [dbo].[Job] ([JobId], [Name], [JobType], [Frequency], [Interval], [StartDate], [EndDate], [IsEnabled], [IsStarted], [IsExecuting], [NextExecution], [RetentionHistory], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
+VALUES (1, N'Sample Daily Job', N'Oqtane.Infrastructure.SampleJob, Oqtane.Server', N'd', 1, null, null, 1, 0, 0, null, 10, '', getdate(), '', getdate())
+GO
+SET IDENTITY_INSERT [dbo].[Job] OFF
+GO
+
diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs
index 3d5a8497..a824dfbc 100644
--- a/Oqtane.Server/Startup.cs
+++ b/Oqtane.Server/Startup.cs
@@ -100,8 +100,8 @@ namespace Oqtane.Server
services.AddScoped();
services.AddScoped();
services.AddScoped();
- services.AddScoped();
- services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
services.AddSingleton();
@@ -173,8 +173,8 @@ namespace Oqtane.Server
services.AddTransient();
services.AddTransient();
services.AddTransient();
- services.AddTransient();
- services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
// get list of loaded assemblies
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
@@ -382,8 +382,8 @@ namespace Oqtane.Server
services.AddTransient();
services.AddTransient();
services.AddTransient();
- services.AddTransient();
- services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
// get list of loaded assemblies
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
diff --git a/Oqtane.Shared/Models/Schedule.cs b/Oqtane.Shared/Models/Job.cs
similarity index 69%
rename from Oqtane.Shared/Models/Schedule.cs
rename to Oqtane.Shared/Models/Job.cs
index f82dfab3..2d1f40fc 100644
--- a/Oqtane.Shared/Models/Schedule.cs
+++ b/Oqtane.Shared/Models/Job.cs
@@ -2,15 +2,17 @@
namespace Oqtane.Models
{
- public class Schedule : IAuditable
+ public class Job : IAuditable
{
- public int ScheduleId { get; set; }
+ public int JobId { get; set; }
public string Name { get; set; }
public string JobType { get; set; }
- public int Period { get; set; }
public string Frequency { get; set; }
+ public int Interval { get; set; }
public DateTime? StartDate { get; set; }
- public bool IsActive { get; set; }
+ public DateTime? EndDate { get; set; }
+ public bool IsEnabled { get; set; }
+ public bool IsStarted { get; set; }
public bool IsExecuting { get; set; }
public DateTime? NextExecution { get; set; }
public int RetentionHistory { get; set; }
diff --git a/Oqtane.Shared/Models/ScheduleLog.cs b/Oqtane.Shared/Models/JobLog.cs
similarity index 62%
rename from Oqtane.Shared/Models/ScheduleLog.cs
rename to Oqtane.Shared/Models/JobLog.cs
index 77096d9b..161d5b54 100644
--- a/Oqtane.Shared/Models/ScheduleLog.cs
+++ b/Oqtane.Shared/Models/JobLog.cs
@@ -2,13 +2,15 @@
namespace Oqtane.Models
{
- public class ScheduleLog
+ public class JobLog
{
- public int ScheduleLogId { get; set; }
- public int ScheduleId { get; set; }
+ public int JobLogId { get; set; }
+ public int JobId { get; set; }
public DateTime StartDate { get; set; }
public DateTime? FinishDate { get; set; }
public bool? Succeeded { get; set; }
public string Notes { get; set; }
+
+ public Job Job { get; set; }
}
}