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">
|
<data name="Installed" xml:space="preserve">
|
||||||
<value>Installed</value>
|
<value>Installed</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Global Replace" xml:space="preserve">
|
||||||
|
<value>Global Replace</value>
|
||||||
|
</data>
|
||||||
</root>
|
</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
|
// iterate through sites for current tenant
|
||||||
List<Site> sites = siteRepository.GetSites().ToList();
|
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 />";
|
log += "Processing Notifications For Site: " + site.Name + "<br />";
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ namespace Oqtane.Infrastructure
|
|||||||
// get site settings
|
// get site settings
|
||||||
var settings = settingRepository.GetSettings(EntityNames.Site, site.SiteId, EntityNames.Host, -1);
|
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;
|
bool valid = true;
|
||||||
if (settingRepository.GetSettingValue(settings, "SMTPAuthentication", "Basic") == "Basic")
|
if (settingRepository.GetSettingValue(settings, "SMTPAuthentication", "Basic") == "Basic")
|
||||||
@@ -290,7 +290,7 @@ namespace Oqtane.Infrastructure
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
log += "Site Deleted Or SMTP Disabled In Site Settings<br />";
|
log += "SMTP Disabled In Site Settings<br />";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Oqtane.Infrastructure
|
|||||||
|
|
||||||
// iterate through sites for current tenant
|
// iterate through sites for current tenant
|
||||||
List<Site> sites = siteRepository.GetSites().ToList();
|
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 />";
|
log += "<br />Processing Site: " + site.Name + "<br />";
|
||||||
int count;
|
int count;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ namespace Oqtane.Infrastructure
|
|||||||
var searchService = provider.GetRequiredService<ISearchService>();
|
var searchService = provider.GetRequiredService<ISearchService>();
|
||||||
|
|
||||||
var sites = siteRepository.GetSites().ToList();
|
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 />";
|
log += $"Indexing Site: {site.Name}<br />";
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ namespace Oqtane.Infrastructure
|
|||||||
|
|
||||||
// get primary site
|
// get primary site
|
||||||
var primarySite = sites.FirstOrDefault(item => item.SiteId == siteGroup.PrimarySiteId);
|
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
|
// update flag to prevent job from processing group again
|
||||||
siteGroup.Synchronize = false;
|
siteGroup.Synchronize = false;
|
||||||
@@ -112,7 +112,7 @@ namespace Oqtane.Infrastructure
|
|||||||
}
|
}
|
||||||
else
|
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)
|
// host pages (order starts at 51)
|
||||||
pageTemplates.Add(new PageTemplate
|
pageTemplates.Add(new PageTemplate
|
||||||
|
|||||||
@@ -93,6 +93,9 @@ namespace Oqtane.Infrastructure
|
|||||||
case "10.0.4":
|
case "10.0.4":
|
||||||
Upgrade_10_0_4(tenant, scope);
|
Upgrade_10_0_4(tenant, scope);
|
||||||
break;
|
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");
|
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)
|
private void AddPagesToSites(IServiceScope scope, Tenant tenant, List<PageTemplate> pageTemplates)
|
||||||
{
|
{
|
||||||
var tenants = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
var tenants = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ namespace Oqtane.Repository
|
|||||||
moduledefinition = new ModuleDefinition
|
moduledefinition = new ModuleDefinition
|
||||||
{
|
{
|
||||||
Name = Utilities.GetTypeNameLastSegment(modulecontroltype.Namespace, 0),
|
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" : "")
|
Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : "")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -434,6 +434,12 @@ namespace Oqtane.Repository
|
|||||||
moduledefinition.ControlTypeRoutes += (action + "=" + modulecontroltype.FullName + ", " + modulecontroltype.Assembly.GetName().Name + ";");
|
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
|
// check for Page attribute
|
||||||
var routeAttributes = modulecontroltype.GetCustomAttributes(typeof(RouteAttribute), true).Cast<RouteAttribute>();
|
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