add Job Tasks to enable the execution of adhoc asynchronous site-based workloads

This commit is contained in:
sbwalker
2026-02-19 08:23:11 -05:00
parent 13a58ed099
commit 0fd97d34d9
23 changed files with 633 additions and 252 deletions

View File

@@ -0,0 +1,13 @@
using System;
using System.Threading.Tasks;
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public interface IJobTask
{
string ExecuteTask(IServiceProvider provider, Site site, string parameters);
Task<string> ExecuteTaskAsync(IServiceProvider provider, Site site, string parameters);
}
}

View File

@@ -1,178 +0,0 @@
using System;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Models;
using Oqtane.Modules;
using Oqtane.Repository;
using Oqtane.Shared;
namespace Oqtane.Infrastructure
{
public class GlobalReplaceJob : HostedServiceBase
{
public GlobalReplaceJob(IServiceScopeFactory serviceScopeFactory) : base(serviceScopeFactory)
{
Name = "Global Replace Job";
Frequency = "m"; // run every minute.
Interval = 1;
IsEnabled = true;
}
public override async Task<string> ExecuteJobAsync(IServiceProvider provider)
{
string log = "";
// get services
var siteRepository = provider.GetRequiredService<ISiteRepository>();
var settingRepository = provider.GetRequiredService<ISettingRepository>();
var tenantManager = provider.GetRequiredService<ITenantManager>();
var pageRepository = provider.GetRequiredService<IPageRepository>();
var pageModuleRepository = provider.GetRequiredService<IPageModuleRepository>();
var sites = siteRepository.GetSites().ToList();
foreach (var site in sites.Where(item => !item.IsDeleted))
{
log += $"Processing Site: {site.Name}<br />";
// get global replace items in order by date/time submitted
var globalReplaceSettings = settingRepository.GetSettings(EntityNames.Site, site.SiteId)
.Where(item => item.SettingName.StartsWith("GlobalReplace_"))
.OrderBy(item => item.SettingName);
if (globalReplaceSettings != null && globalReplaceSettings.Any())
{
// get first item
var globalReplace = JsonSerializer.Deserialize<GlobalReplace>(globalReplaceSettings.First().SettingValue);
var find = globalReplace.Find;
var replace = globalReplace.Replace;
var comparisonType = (globalReplace.CaseSensitive) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
log += $"Replacing: '{find}' With: '{replace}' Case Sensitive: {globalReplace.CaseSensitive}<br />";
var tenantId = tenantManager.GetTenant().TenantId;
tenantManager.SetAlias(tenantId, site.SiteId);
var changed = false;
if (site.Name != null && site.Name.Contains(find, comparisonType))
{
site.Name = site.Name.Replace(find, replace, comparisonType);
changed = true;
}
if (site.HeadContent != null && site.HeadContent.Contains(find, comparisonType))
{
site.HeadContent = site.HeadContent.Replace(find, replace, comparisonType);
changed = true;
}
if (site.BodyContent != null && site.BodyContent.Contains(find, comparisonType))
{
site.BodyContent = site.BodyContent.Replace(find, replace, comparisonType);
changed = true;
}
if (changed && globalReplace.Site)
{
siteRepository.UpdateSite(site);
log += $"Site Updated<br />";
}
var pages = pageRepository.GetPages(site.SiteId);
var pageModules = pageModuleRepository.GetPageModules(site.SiteId);
// iterate pages
foreach (var page in pages)
{
// page properties
changed = false;
if (page.Name != null && page.Name.Contains(find, comparisonType))
{
page.Name = page.Name.Replace(find, replace, comparisonType);
changed = true;
}
if (page.Title != null && page.Title.Contains(find, comparisonType))
{
page.Title = page.Title.Replace(find, replace, comparisonType);
changed = true;
}
if (page.HeadContent != null && page.HeadContent.Contains(find, comparisonType))
{
page.HeadContent = page.HeadContent.Replace(find, replace, comparisonType);
changed = true;
}
if (page.BodyContent != null && page.BodyContent.Contains(find, comparisonType))
{
page.BodyContent = page.BodyContent.Replace(find, replace, comparisonType);
changed = true;
}
if (changed && globalReplace.Pages)
{
pageRepository.UpdatePage(page);
log += $"Page Updated: /{page.Path}<br />";
}
foreach (var pageModule in pageModules.Where(item => item.PageId == page.PageId))
{
// pagemodule properties
changed = false;
if (pageModule.Title != null && pageModule.Title.Contains(find, comparisonType))
{
pageModule.Title = pageModule.Title.Replace(find, replace, comparisonType);
changed = true;
}
if (pageModule.Header != null && pageModule.Header.Contains(find, comparisonType))
{
pageModule.Header = pageModule.Header.Replace(find, replace, comparisonType);
changed = true;
}
if (pageModule.Footer != null && pageModule.Footer.Contains(find, comparisonType))
{
pageModule.Footer = pageModule.Footer.Replace(find, replace, comparisonType);
changed = true;
}
if (changed && globalReplace.Modules)
{
pageModuleRepository.UpdatePageModule(pageModule);
log += $"Module Updated: {pageModule.Title} - /{page.Path}<br />";
}
// module content
if (pageModule.Module.ModuleDefinition != null && pageModule.Module.ModuleDefinition.ServerManagerType != "")
{
Type moduleType = Type.GetType(pageModule.Module.ModuleDefinition.ServerManagerType);
if (moduleType != null && moduleType.GetInterface(nameof(IPortable)) != null)
{
try
{
// module content
var moduleObject = ActivatorUtilities.CreateInstance(provider, moduleType);
var moduleContent = ((IPortable)moduleObject).ExportModule(pageModule.Module);
if (!string.IsNullOrEmpty(moduleContent) && moduleContent.Contains(find, comparisonType) && globalReplace.Content)
{
moduleContent = moduleContent.Replace(find, replace, comparisonType);
((IPortable)moduleObject).ImportModule(pageModule.Module, moduleContent, pageModule.Module.ModuleDefinition.Version);
log += $"Module Content Updated: {pageModule.Title} - /{page.Path}<br />";
}
}
catch (Exception ex)
{
log += $"Error Processing Module {pageModule.Module.ModuleDefinition.Name} - {ex.Message}<br />";
}
}
}
}
}
// remove global replace setting to prevent reprocessing
settingRepository.DeleteSetting(EntityNames.Site, globalReplaceSettings.First().SettingId);
}
else
{
log += $"No Criteria Provided<br />";
}
}
return log;
}
}
}

View File

@@ -0,0 +1,83 @@
using System;
using System.Diagnostics.Eventing.Reader;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Repository;
namespace Oqtane.Infrastructure
{
public class TaskJob : HostedServiceBase
{
public TaskJob(IServiceScopeFactory serviceScopeFactory) : base(serviceScopeFactory)
{
Name = "Task Job";
Frequency = "m"; // run every minute
Interval = 1;
IsEnabled = true;
}
// job is executed for each tenant in installation
public override async Task<string> ExecuteJobAsync(IServiceProvider provider)
{
var log = "";
var tenantManager = provider.GetRequiredService<ITenantManager>();
var tenant = tenantManager.GetTenant();
// iterate through sites for current tenant
var siteRepository = provider.GetRequiredService<ISiteRepository>();
var sites = siteRepository.GetSites().ToList();
foreach (var site in sites.Where(item => !item.IsDeleted))
{
log += $"Processing Site: {site.Name}<br />";
// get incomplete tasks for site
var jobTaskRepository = provider.GetRequiredService<IJobTaskRepository>();
var tasks = jobTaskRepository.GetJobTasks(site.SiteId).ToList();
if (tasks != null && tasks.Any())
{
foreach (var task in tasks)
{
log += $"Executing Task: {task.Name}<br />";
Type taskType = Type.GetType(task.Type);
if (taskType != null && taskType.GetInterface(nameof(IJobTask)) != null)
{
try
{
tenantManager.SetAlias(tenant.TenantId, site.SiteId);
var taskObject = ActivatorUtilities.CreateInstance(provider, taskType);
var taskLog = ((IJobTask)taskObject).ExecuteTask(provider, site, task.Parameters);
taskLog += await ((IJobTask)taskObject).ExecuteTaskAsync(provider, site, task.Parameters);
task.Status = taskLog;
}
catch (Exception ex)
{
task.Status = "Error: " + ex.Message;
}
}
else
{
task.Status = $"Error: Task {task.Name} Has An Invalid Type {task.Type}<br />";
}
// update task
task.IsCompleted = true;
jobTaskRepository.UpdateJobTask(task);
log += task.Status + "<br />";
}
}
else
{
log += "No Tasks To Execute<br />";
}
}
return log;
}
}
}

View File

@@ -0,0 +1,150 @@
using System;
using System.Linq;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Models;
using Oqtane.Modules;
using Oqtane.Repository;
namespace Oqtane.Infrastructure
{
public class GlobalReplaceTask : JobTaskBase
{
public override string ExecuteTask(IServiceProvider provider, Site site, string parameters)
{
string log = "";
// get services
var siteRepository = provider.GetRequiredService<ISiteRepository>();
var pageRepository = provider.GetRequiredService<IPageRepository>();
var pageModuleRepository = provider.GetRequiredService<IPageModuleRepository>();
if (!string.IsNullOrEmpty(parameters))
{
// get parameters
var globalReplace = JsonSerializer.Deserialize<GlobalReplace>(parameters);
var find = globalReplace.Find;
var replace = globalReplace.Replace;
var comparisonType = (globalReplace.CaseSensitive) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
log += $"Replacing: '{find}' With: '{replace}' Case Sensitive: {globalReplace.CaseSensitive}<br />";
// site properties
site = siteRepository.GetSite(site.SiteId);
var changed = false;
if (site.Name != null && site.Name.Contains(find, comparisonType))
{
site.Name = site.Name.Replace(find, replace, comparisonType);
changed = true;
}
if (site.HeadContent != null && site.HeadContent.Contains(find, comparisonType))
{
site.HeadContent = site.HeadContent.Replace(find, replace, comparisonType);
changed = true;
}
if (site.BodyContent != null && site.BodyContent.Contains(find, comparisonType))
{
site.BodyContent = site.BodyContent.Replace(find, replace, comparisonType);
changed = true;
}
if (changed && globalReplace.Site)
{
siteRepository.UpdateSite(site);
log += $"Site Updated<br />";
}
var pages = pageRepository.GetPages(site.SiteId).ToList();
var pageModules = pageModuleRepository.GetPageModules(site.SiteId).ToList();
// iterate pages
foreach (var page in pages)
{
// page properties
changed = false;
if (page.Name != null && page.Name.Contains(find, comparisonType))
{
page.Name = page.Name.Replace(find, replace, comparisonType);
changed = true;
}
if (page.Title != null && page.Title.Contains(find, comparisonType))
{
page.Title = page.Title.Replace(find, replace, comparisonType);
changed = true;
}
if (page.HeadContent != null && page.HeadContent.Contains(find, comparisonType))
{
page.HeadContent = page.HeadContent.Replace(find, replace, comparisonType);
changed = true;
}
if (page.BodyContent != null && page.BodyContent.Contains(find, comparisonType))
{
page.BodyContent = page.BodyContent.Replace(find, replace, comparisonType);
changed = true;
}
if (changed && globalReplace.Pages)
{
pageRepository.UpdatePage(page);
log += $"Page Updated: /{page.Path}<br />";
}
foreach (var pageModule in pageModules.Where(item => item.PageId == page.PageId))
{
// module properties
changed = false;
if (pageModule.Title != null && pageModule.Title.Contains(find, comparisonType))
{
pageModule.Title = pageModule.Title.Replace(find, replace, comparisonType);
changed = true;
}
if (pageModule.Header != null && pageModule.Header.Contains(find, comparisonType))
{
pageModule.Header = pageModule.Header.Replace(find, replace, comparisonType);
changed = true;
}
if (pageModule.Footer != null && pageModule.Footer.Contains(find, comparisonType))
{
pageModule.Footer = pageModule.Footer.Replace(find, replace, comparisonType);
changed = true;
}
if (changed && globalReplace.Modules)
{
pageModuleRepository.UpdatePageModule(pageModule);
log += $"Module Updated: {pageModule.Title} - /{page.Path}<br />";
}
// module content
if (pageModule.Module.ModuleDefinition != null && pageModule.Module.ModuleDefinition.ServerManagerType != "")
{
Type moduleType = Type.GetType(pageModule.Module.ModuleDefinition.ServerManagerType);
if (moduleType != null && moduleType.GetInterface(nameof(IPortable)) != null)
{
try
{
var moduleObject = ActivatorUtilities.CreateInstance(provider, moduleType);
var moduleContent = ((IPortable)moduleObject).ExportModule(pageModule.Module);
if (!string.IsNullOrEmpty(moduleContent) && moduleContent.Contains(find, comparisonType) && globalReplace.Content)
{
moduleContent = moduleContent.Replace(find, replace, comparisonType);
((IPortable)moduleObject).ImportModule(pageModule.Module, moduleContent, pageModule.Module.ModuleDefinition.Version);
log += $"Module Content Updated: {pageModule.Title} - /{page.Path}<br />";
}
}
catch (Exception ex)
{
log += $"Error Processing Module {pageModule.Module.ModuleDefinition.Name} - {ex.Message}<br />";
}
}
}
}
}
}
else
{
log += $"No Criteria Provided<br />";
}
return log;
}
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Managers;
using Oqtane.Models;
using Oqtane.Repository;
namespace Oqtane.Infrastructure
{
public class ImportUsersTask : JobTaskBase
{
public override async Task<string> ExecuteTaskAsync(IServiceProvider provider, Site site, string parameters)
{
string log = "";
if (!string.IsNullOrEmpty(parameters) && parameters.Contains(":"))
{
var fileId = int.Parse(parameters.Split(':')[0]);
var notify = bool.Parse(parameters.Split(':')[1]);
var fileRepository = provider.GetRequiredService<IFileRepository>();
var userManager = provider.GetRequiredService<IUserManager>();
var file = fileRepository.GetFile(fileId);
if (file != null)
{
var filePath = fileRepository.GetFilePath(file);
log += $"Importing Users From {filePath}<br />";
var result = await userManager.ImportUsers(site.SiteId, filePath, notify);
if (result["Success"] == "True")
{
log += $"{result["Users"]} Users Imported<br />";
}
else
{
log += $"User Import Failed<br />";
}
}
else
{
log += $"Import Users FileId {fileId} Does Not Exist<br />";
}
}
return log;
}
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Threading.Tasks;
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public class JobTaskBase : IJobTask
{
public virtual string ExecuteTask(IServiceProvider provider, Site site, string parameters)
{
return "";
}
public virtual Task<string> ExecuteTaskAsync(IServiceProvider provider, Site site, string parameters)
{
return Task.FromResult(string.Empty);
}
}
}