add new global replace service for bulk updating content
This commit is contained in:
117
Oqtane.Client/Modules/Admin/GlobalReplace/Index.razor
Normal file
117
Oqtane.Client/Modules/Admin/GlobalReplace/Index.razor
Normal file
@@ -0,0 +1,117 @@
|
||||
@namespace Oqtane.Modules.Admin.GlobalReplace
|
||||
@using System.Text.Json
|
||||
@inherits ModuleBase
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="find" HelpText="Specify the content which needs to be replaced" ResourceKey="Find">Find What: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="find" class="form-control" @bind="@_find" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="replace" HelpText="Specify the replacement content" ResourceKey="Replace">Replace With: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="replace" class="form-control" @bind="@_replace" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="casesensitive" HelpText="Specify if the replacement operation should be case sensitive" ResourceKey="CaseSensitive">Match Case? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="casesensitive" class="form-select" @bind="@_caseSensitive">
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="site" HelpText="Specify if site properties should be updated (ie. name, headcontent, bodycontent)" ResourceKey="Site">Site Properties? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="site" class="form-select" @bind="@_site">
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="pages" HelpText="Specify if page properties should be updated (ie. name, title, headcontent, bodycontent)" ResourceKey="Pages">Page Properties? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="pages" class="form-select" @bind="@_pages">
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="modules" HelpText="Specify if module properties should be updated (ie. title, header, footer)" ResourceKey="Modules">Module Properties? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="modules" class="form-select" @bind="@_modules">
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="content" HelpText="Specify if module content should be updated" ResourceKey="Content">Module Content? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="content" class="form-select" @bind="@_content">
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br /><br />
|
||||
<ActionDialog Header="Global Replace" Message="This Operation is Permanent. Are You Sure You Wish To Proceed?" Action="Replace" Class="btn btn-primary" OnClick="@(async () => await Save())" ResourceKey="GlobalReplace" />
|
||||
<br /><br />
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
public override string Title => "Global Replace";
|
||||
|
||||
private string _find;
|
||||
private string _replace;
|
||||
private string _caseSensitive = "True";
|
||||
private string _site = "True";
|
||||
private string _pages = "True";
|
||||
private string _modules = "True";
|
||||
private string _content = "True";
|
||||
|
||||
private async Task Save()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_find) && !string.IsNullOrEmpty(_replace))
|
||||
{
|
||||
var replace = new GlobalReplace
|
||||
{
|
||||
Find = _find,
|
||||
Replace = _replace,
|
||||
CaseSensitive = bool.Parse(_caseSensitive),
|
||||
Site = bool.Parse(_site),
|
||||
Pages = bool.Parse(_pages),
|
||||
Modules = bool.Parse(_modules),
|
||||
Content = bool.Parse(_content)
|
||||
};
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "GlobalReplace_" + DateTime.UtcNow.ToString("yyyyMMddHHmmss"), JsonSerializer.Serialize(replace));
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
AddModuleMessage(Localizer["Success.Save"], MessageType.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Saving Global Replace Settings {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Save"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
174
Oqtane.Client/Resources/Modules/Admin/GlobalReplace/Index.resx
Normal file
174
Oqtane.Client/Resources/Modules/Admin/GlobalReplace/Index.resx
Normal file
@@ -0,0 +1,174 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="CaseSensitive.Text" xml:space="preserve">
|
||||
<value>Match Case?</value>
|
||||
</data>
|
||||
<data name="CaseSensitive.HelpText" xml:space="preserve">
|
||||
<value>Specify if the replacement operation should be case sensitive</value>
|
||||
</data>
|
||||
<data name="Content.Text" xml:space="preserve">
|
||||
<value>Module Content?</value>
|
||||
</data>
|
||||
<data name="Content.HelpText" xml:space="preserve">
|
||||
<value>Specify if module content should be updated</value>
|
||||
</data>
|
||||
<data name="Pages.Text" xml:space="preserve">
|
||||
<value>Page Properties?</value>
|
||||
</data>
|
||||
<data name="Pages.HelpText" xml:space="preserve">
|
||||
<value>Specify if page properties should be updated (ie. name, title, headcontent, bodycontent)</value>
|
||||
</data>
|
||||
<data name="Site.Text" xml:space="preserve">
|
||||
<value>Site Properties?</value>
|
||||
</data>
|
||||
<data name="Site.HelpText" xml:space="preserve">
|
||||
<value>Specify if site properties should be updated (ie. name, headcontent, bodycontent)</value>
|
||||
</data>
|
||||
<data name="Replace.Text" xml:space="preserve">
|
||||
<value>Replace With:</value>
|
||||
</data>
|
||||
<data name="Replace.HelpText" xml:space="preserve">
|
||||
<value>Specify the replacement content</value>
|
||||
</data>
|
||||
<data name="Modules.Text" xml:space="preserve">
|
||||
<value>Module Properties?</value>
|
||||
</data>
|
||||
<data name="Modules.HelpText" xml:space="preserve">
|
||||
<value>Specify if module properties should be updated (ie. title, header, footer)</value>
|
||||
</data>
|
||||
<data name="Success.Save" xml:space="preserve">
|
||||
<value>Your Global Replace Request Has Been Submitted And Will Be Executed Shortly</value>
|
||||
</data>
|
||||
<data name="Error.Save" xml:space="preserve">
|
||||
<value>Error Saving Global Replace</value>
|
||||
</data>
|
||||
<data name="Find.HelpText" xml:space="preserve">
|
||||
<value>Specify the content which needs to be replaced</value>
|
||||
</data>
|
||||
<data name="Find.Text" xml:space="preserve">
|
||||
<value>Find What:</value>
|
||||
</data>
|
||||
<data name="GlobalReplace.Header" xml:space="preserve">
|
||||
<value>Global Replace</value>
|
||||
</data>
|
||||
<data name="GlobalReplace.Message" xml:space="preserve">
|
||||
<value>This Operation is Permanent. Are You Sure You Wish To Proceed?</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -483,4 +483,7 @@
|
||||
<data name="Installed" xml:space="preserve">
|
||||
<value>Installed</value>
|
||||
</data>
|
||||
<data name="Global Replace" xml:space="preserve">
|
||||
<value>Global Replace</value>
|
||||
</data>
|
||||
</root>
|
||||
178
Oqtane.Server/Infrastructure/Jobs/GlobalReplaceJob.cs
Normal file
178
Oqtane.Server/Infrastructure/Jobs/GlobalReplaceJob.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ namespace Oqtane.Infrastructure
|
||||
|
||||
// iterate through sites for current tenant
|
||||
List<Site> sites = siteRepository.GetSites().ToList();
|
||||
foreach (Site site in sites)
|
||||
foreach (Site site in sites.Where(item => !item.IsDeleted))
|
||||
{
|
||||
log += "Processing Notifications For Site: " + site.Name + "<br />";
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Oqtane.Infrastructure
|
||||
// get site settings
|
||||
var settings = settingRepository.GetSettings(EntityNames.Site, site.SiteId, EntityNames.Host, -1);
|
||||
|
||||
if (!site.IsDeleted && settingRepository.GetSettingValue(settings, "SMTPEnabled", "True") == "True")
|
||||
if (settingRepository.GetSettingValue(settings, "SMTPEnabled", "True") == "True")
|
||||
{
|
||||
bool valid = true;
|
||||
if (settingRepository.GetSettingValue(settings, "SMTPAuthentication", "Basic") == "Basic")
|
||||
@@ -290,7 +290,7 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
else
|
||||
{
|
||||
log += "Site Deleted Or SMTP Disabled In Site Settings<br />";
|
||||
log += "SMTP Disabled In Site Settings<br />";
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Oqtane.Infrastructure
|
||||
|
||||
// iterate through sites for current tenant
|
||||
List<Site> sites = siteRepository.GetSites().ToList();
|
||||
foreach (Site site in sites)
|
||||
foreach (Site site in sites.Where(item => !item.IsDeleted))
|
||||
{
|
||||
log += "<br />Processing Site: " + site.Name + "<br />";
|
||||
int count;
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Oqtane.Infrastructure
|
||||
var searchService = provider.GetRequiredService<ISearchService>();
|
||||
|
||||
var sites = siteRepository.GetSites().ToList();
|
||||
foreach (var site in sites)
|
||||
foreach (var site in sites.Where(item => !item.IsDeleted))
|
||||
{
|
||||
log += $"Indexing Site: {site.Name}<br />";
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Oqtane.Infrastructure
|
||||
|
||||
// get primary site
|
||||
var primarySite = sites.FirstOrDefault(item => item.SiteId == siteGroup.PrimarySiteId);
|
||||
if (primarySite != null)
|
||||
if (primarySite != null && !primarySite.IsDeleted)
|
||||
{
|
||||
// update flag to prevent job from processing group again
|
||||
siteGroup.Synchronize = false;
|
||||
@@ -112,7 +112,7 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
else
|
||||
{
|
||||
log += $"Site Group {siteGroup.Name} Has A PrimarySiteId {siteGroup.PrimarySiteId} Which Does Not Exist<br />";
|
||||
log += $"Site Group {siteGroup.Name} Has A PrimarySiteId {siteGroup.PrimarySiteId} Which Does Not Exist Or Is Deleted<br />";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -607,6 +607,34 @@ namespace Oqtane.Infrastructure.SiteTemplates
|
||||
}
|
||||
}
|
||||
});
|
||||
pageTemplates.Add(new PageTemplate
|
||||
{
|
||||
Name = "Global Replace",
|
||||
Parent = "Admin",
|
||||
Order = 23,
|
||||
Path = "admin/replace",
|
||||
Icon = Icons.LoopSquare,
|
||||
IsNavigation = false,
|
||||
IsPersonalizable = false,
|
||||
PermissionList = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
PageTemplateModules = new List<PageTemplateModule>
|
||||
{
|
||||
new PageTemplateModule
|
||||
{
|
||||
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.GlobalReplace.Index).ToModuleDefinitionName(), Title = "Global Replace", Pane = PaneNames.Default,
|
||||
PermissionList = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
Content = ""
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// host pages (order starts at 51)
|
||||
pageTemplates.Add(new PageTemplate
|
||||
|
||||
@@ -93,6 +93,9 @@ namespace Oqtane.Infrastructure
|
||||
case "10.0.4":
|
||||
Upgrade_10_0_4(tenant, scope);
|
||||
break;
|
||||
case "10.1.0":
|
||||
Upgrade_10_1_0(tenant, scope);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -602,6 +605,45 @@ namespace Oqtane.Infrastructure
|
||||
RemoveAssemblies(tenant, assemblies, "10.0.4");
|
||||
}
|
||||
|
||||
private void Upgrade_10_1_0(Tenant tenant, IServiceScope scope)
|
||||
{
|
||||
var pageTemplates = new List<PageTemplate>
|
||||
{
|
||||
new PageTemplate
|
||||
{
|
||||
Update = false,
|
||||
Name = "Global Replace",
|
||||
Parent = "Admin",
|
||||
Order = 23,
|
||||
Path = "admin/replace",
|
||||
Icon = Icons.LoopSquare,
|
||||
IsNavigation = false,
|
||||
IsPersonalizable = false,
|
||||
PermissionList = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
PageTemplateModules = new List<PageTemplateModule>
|
||||
{
|
||||
new PageTemplateModule
|
||||
{
|
||||
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.GlobalReplace.Index).ToModuleDefinitionName(), Title = "Global Replace", Pane = PaneNames.Default,
|
||||
PermissionList = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
Content = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddPagesToSites(scope, tenant, pageTemplates);
|
||||
}
|
||||
|
||||
|
||||
private void AddPagesToSites(IServiceScope scope, Tenant tenant, List<PageTemplate> pageTemplates)
|
||||
{
|
||||
var tenants = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
||||
|
||||
@@ -354,7 +354,7 @@ namespace Oqtane.Repository
|
||||
moduledefinition = new ModuleDefinition
|
||||
{
|
||||
Name = Utilities.GetTypeNameLastSegment(modulecontroltype.Namespace, 0),
|
||||
Description = "Manage " + Utilities.GetTypeNameLastSegment(modulecontroltype.Namespace, 0),
|
||||
Description = Utilities.GetTypeNameLastSegment(modulecontroltype.Namespace, 0),
|
||||
Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : "")
|
||||
};
|
||||
}
|
||||
@@ -434,6 +434,12 @@ namespace Oqtane.Repository
|
||||
moduledefinition.ControlTypeRoutes += (action + "=" + modulecontroltype.FullName + ", " + modulecontroltype.Assembly.GetName().Name + ";");
|
||||
}
|
||||
}
|
||||
// module title
|
||||
if (modulecontroltype.Name == Constants.DefaultAction && !string.IsNullOrEmpty(modulecontrolobject.Title))
|
||||
{
|
||||
moduledefinition.Name = modulecontrolobject.Title;
|
||||
moduledefinition.Description = "Manage " + moduledefinition.Name;
|
||||
}
|
||||
|
||||
// check for Page attribute
|
||||
var routeAttributes = modulecontroltype.GetCustomAttributes(typeof(RouteAttribute), true).Cast<RouteAttribute>();
|
||||
|
||||
43
Oqtane.Shared/Models/GlobalReplace.cs
Normal file
43
Oqtane.Shared/Models/GlobalReplace.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
namespace Oqtane.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a global replace operation
|
||||
/// </summary>
|
||||
public class GlobalReplace
|
||||
{
|
||||
/// <summary>
|
||||
/// text to replace
|
||||
/// </summary>
|
||||
public string Find { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// replacement text
|
||||
/// </summary>
|
||||
public string Replace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// case sensitive
|
||||
/// </summary>
|
||||
public bool CaseSensitive { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// include site properties
|
||||
/// </summary>
|
||||
public bool Site { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// include page properties
|
||||
/// </summary>
|
||||
public bool Pages { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// include module properties
|
||||
/// </summary>
|
||||
public bool Modules { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// include content
|
||||
/// </summary>
|
||||
public bool Content { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user