improvements to site groups

This commit is contained in:
sbwalker
2026-02-10 08:55:11 -05:00
parent ddd6dfc475
commit 6f2e676c00
7 changed files with 165 additions and 127 deletions

View File

@@ -480,10 +480,11 @@
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="grouptype" HelpText="The site group type (ie. synchronization, localization)" ResourceKey="GroupType">Type: </Label> <Label Class="col-sm-3" For="grouptype" HelpText="The site group type (ie. synchronization, comparison, localization)" ResourceKey="GroupType">Type: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<select id="grouptype" class="form-select" @bind="@_groupType"> <select id="grouptype" class="form-select" @bind="@_groupType">
<option value="@SiteGroupTypes.Synchronization">@Localizer[@SiteGroupTypes.Synchronization]</option> <option value="@SiteGroupTypes.Synchronization">@Localizer[@SiteGroupTypes.Synchronization]</option>
<option value="@SiteGroupTypes.Comparison">@Localizer[@SiteGroupTypes.Comparison]</option>
<option value="@SiteGroupTypes.Localization">@Localizer[SiteGroupTypes.Localization]</option> <option value="@SiteGroupTypes.Localization">@Localizer[SiteGroupTypes.Localization]</option>
</select> </select>
</div> </div>
@@ -517,7 +518,7 @@
@if (_siteGroupId != -1 && _siteId != -1) @if (_siteGroupId != -1 && _siteId != -1)
{ {
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="primary" HelpText="Indicates if the selected site is the primary member of the site group" ResourceKey="Primary">Primary? </Label> <Label Class="col-sm-3" For="primary" HelpText="Indicates if the selected member is the primary site of the site group" ResourceKey="Primary">Primary? </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<select id="primary" class="form-select" @bind="@_primary"> <select id="primary" class="form-select" @bind="@_primary">
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
@@ -525,19 +526,10 @@
</select> </select>
</div> </div>
</div> </div>
@if (_primary == "False" && _groupType == SiteGroupTypes.Synchronization) @if (_primary == "False" && (_groupType == SiteGroupTypes.Synchronization || _groupType == SiteGroupTypes.Comparison))
{ {
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="notify" HelpText="Specifies if site administrators should be notified of any synchronization activity" ResourceKey="Notify">Notify? </Label> <Label Class="col-sm-3" For="synchronized" HelpText="The date/time when the site was last synchronized" ResourceKey="Synchronized">Synchronized: </Label>
<div class="col-sm-9">
<select id="notify" class="form-select" @bind="@_notify">
<option value="False">@SharedLocalizer["No"]</option>
<option value="True">@SharedLocalizer["Yes"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="synchronized" HelpText="The date/time of the last synchronization for the site" ResourceKey="Synchronized">Synchronized: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<div class="input-group"> <div class="input-group">
<input id="synchronized" class="form-control" @bind="@_synchronized" disabled /> <input id="synchronized" class="form-control" @bind="@_synchronized" disabled />
@@ -678,7 +670,6 @@
private string _groupName = string.Empty; private string _groupName = string.Empty;
private string _groupType = SiteGroupTypes.Synchronization; private string _groupType = SiteGroupTypes.Synchronization;
private string _primary = "True"; private string _primary = "True";
private string _notify = "True";
private string _synchronized = string.Empty; private string _synchronized = string.Empty;
private bool _addSiteGroup = false; private bool _addSiteGroup = false;
private bool _addSiteGroupMember = false; private bool _addSiteGroupMember = false;
@@ -1304,7 +1295,6 @@
if (siteGroupMember != null) if (siteGroupMember != null)
{ {
_primary = (siteGroupMember.SiteGroup.PrimarySiteId == _siteId) ? "True" : "False"; _primary = (siteGroupMember.SiteGroup.PrimarySiteId == _siteId) ? "True" : "False";
_notify = siteGroupMember.Notify.ToString();
_synchronized = UtcToLocal(siteGroupMember.SynchronizedOn).ToString(); _synchronized = UtcToLocal(siteGroupMember.SynchronizedOn).ToString();
} }
} }
@@ -1317,7 +1307,6 @@
if (siteGroupMember != null) if (siteGroupMember != null)
{ {
_primary = (siteGroupMember.SiteGroup.PrimarySiteId == _siteId) ? "True" : "False"; _primary = (siteGroupMember.SiteGroup.PrimarySiteId == _siteId) ? "True" : "False";
_notify = siteGroupMember.Notify.ToString();
_synchronized = UtcToLocal(siteGroupMember.SynchronizedOn).ToString(); _synchronized = UtcToLocal(siteGroupMember.SynchronizedOn).ToString();
} }
StateHasChanged(); StateHasChanged();
@@ -1341,11 +1330,16 @@
private async Task SaveSiteGroupMember() private async Task SaveSiteGroupMember()
{ {
if (string.IsNullOrEmpty(_groupName))
{
AddModuleMessage(Localizer["Message.Required.GroupName"], MessageType.Warning);
await ScrollToPageTop();
return;
}
SiteGroup siteGroup = null; SiteGroup siteGroup = null;
if (_siteGroupId == -1) if (_siteGroupId == -1)
{
if (!string.IsNullOrEmpty(_groupName))
{ {
siteGroup = new SiteGroup siteGroup = new SiteGroup
{ {
@@ -1356,11 +1350,10 @@
}; };
siteGroup = await SiteGroupService.AddSiteGroupAsync(siteGroup); siteGroup = await SiteGroupService.AddSiteGroupAsync(siteGroup);
} }
}
else else
{ {
siteGroup = _siteGroups.FirstOrDefault(item => item.SiteGroupId == _siteGroupId); siteGroup = _siteGroups.FirstOrDefault(item => item.SiteGroupId == _siteGroupId);
if (siteGroup != null && !string.IsNullOrEmpty(_groupName)) if (siteGroup != null)
{ {
siteGroup.Name = _groupName; siteGroup.Name = _groupName;
siteGroup.Type = _groupType; siteGroup.Type = _groupType;
@@ -1374,6 +1367,8 @@
} }
if (siteGroup != null) if (siteGroup != null)
{
if (_siteId != -1)
{ {
var siteGroupMember = await SiteGroupMemberService.GetSiteGroupMemberAsync(_siteId, siteGroup.SiteGroupId); var siteGroupMember = await SiteGroupMemberService.GetSiteGroupMemberAsync(_siteId, siteGroup.SiteGroupId);
if (siteGroupMember == null) if (siteGroupMember == null)
@@ -1381,17 +1376,16 @@
siteGroupMember = new SiteGroupMember siteGroupMember = new SiteGroupMember
{ {
SiteGroupId = siteGroup.SiteGroupId, SiteGroupId = siteGroup.SiteGroupId,
SiteId = _siteId, SiteId = _siteId
Notify = bool.Parse(_notify)
}; };
await SiteGroupMemberService.AddSiteGroupMemberAsync(siteGroupMember); await SiteGroupMemberService.AddSiteGroupMemberAsync(siteGroupMember);
} }
else else
{ {
siteGroupMember.Notify = bool.Parse(_notify);
siteGroupMember.SynchronizedOn = string.IsNullOrEmpty(_synchronized) ? null : siteGroupMember.SynchronizedOn; siteGroupMember.SynchronizedOn = string.IsNullOrEmpty(_synchronized) ? null : siteGroupMember.SynchronizedOn;
await SiteGroupMemberService.UpdateSiteGroupMemberAsync(siteGroupMember); await SiteGroupMemberService.UpdateSiteGroupMemberAsync(siteGroupMember);
} }
}
if (siteGroup.Type == SiteGroupTypes.Synchronization) if (siteGroup.Type == SiteGroupTypes.Synchronization)
{ {
@@ -1407,11 +1401,6 @@
await LoadSiteGroups(); await LoadSiteGroups();
} }
else
{
AddModuleMessage(Localizer["Message.Required.GroupName"], MessageType.Warning);
await ScrollToPageTop();
}
} }
private async Task CancelSiteGroupMember() private async Task CancelSiteGroupMember()

View File

@@ -508,7 +508,7 @@
<value>Primary?</value> <value>Primary?</value>
</data> </data>
<data name="Primary.HelpText" xml:space="preserve"> <data name="Primary.HelpText" xml:space="preserve">
<value>Indicates if the selected site is the primary member of the site group</value> <value>Indicates if the selected member is the primary site of the site group</value>
</data> </data>
<data name="GroupName.Text" xml:space="preserve"> <data name="GroupName.Text" xml:space="preserve">
<value>Name:</value> <value>Name:</value>
@@ -535,17 +535,11 @@
<value>Are You Sure You Wish To Delete This Member From The Site Group?</value> <value>Are You Sure You Wish To Delete This Member From The Site Group?</value>
</data> </data>
<data name="Message.Required.GroupName" xml:space="preserve"> <data name="Message.Required.GroupName" xml:space="preserve">
<value>Group Name Is Required</value> <value>Site Group Name Is Required</value>
</data> </data>
<data name="Message.Site.Synchronize" xml:space="preserve"> <data name="Message.Site.Synchronize" xml:space="preserve">
<value>Site Submitted For Synchronization</value> <value>Site Submitted For Synchronization</value>
</data> </data>
<data name="Notify.Text" xml:space="preserve">
<value>Notify?</value>
</data>
<data name="Notify.HelpText" xml:space="preserve">
<value>Specifies if site administrators should be notified of any synchronization activity</value>
</data>
<data name="Site.Text" xml:space="preserve"> <data name="Site.Text" xml:space="preserve">
<value>Members:</value> <value>Members:</value>
</data> </data>
@@ -559,10 +553,10 @@
<value>Type:</value> <value>Type:</value>
</data> </data>
<data name="GroupType.HelpText" xml:space="preserve"> <data name="GroupType.HelpText" xml:space="preserve">
<value>The site group type (ie. synchronization, localization)</value> <value>The site group type (ie. synchronization, comparison, localization)</value>
</data> </data>
<data name="Synchronized.HelpText" xml:space="preserve"> <data name="Synchronized.HelpText" xml:space="preserve">
<value>The date/time of the last synchronization for the site</value> <value>The date/time when the site was last synchronized</value>
</data> </data>
<data name="Synchronization" xml:space="preserve"> <data name="Synchronization" xml:space="preserve">
<value>Synchronization</value> <value>Synchronization</value>
@@ -570,4 +564,7 @@
<data name="Localization" xml:space="preserve"> <data name="Localization" xml:space="preserve">
<value>Localization</value> <value>Localization</value>
</data> </data>
<data name="Comparison" xml:space="preserve">
<value>Comparison</value>
</data>
</root> </root>

View File

@@ -35,9 +35,10 @@
<button type="button" data-bs-dismiss="offcanvas" class="btn btn-primary col-12" @onclick=@(async () => Navigate("Admin"))>@Localizer["AdminDash"]</button> <button type="button" data-bs-dismiss="offcanvas" class="btn btn-primary col-12" @onclick=@(async () => Navigate("Admin"))>@Localizer["AdminDash"]</button>
</div> </div>
</div> </div>
@if (_siteGroups.Any(item => item.Type == SiteGroupTypes.Synchronization)) @if (_siteGroups.Any(item => item.Type == SiteGroupTypes.Synchronization || item.Type == SiteGroupTypes.Comparison))
{ {
<button type="button" class="btn btn-success col-12 mt-1" @onclick="SynchronizeSite">@Localizer["Synchronize"]</button> <hr class="app-rule" />
<button type="button" class="btn btn-secondary col-12 mt-1" @onclick="SynchronizeSite">@Localizer["Synchronize"]</button>
} }
<hr class="app-rule" /> <hr class="app-rule" />
} }
@@ -641,7 +642,7 @@
private async Task SynchronizeSite() private async Task SynchronizeSite()
{ {
foreach (var group in _siteGroups.Where(item => item.Type == SiteGroupTypes.Synchronization)) foreach (var group in _siteGroups.Where(item => item.Type == SiteGroupTypes.Synchronization || item.Type == SiteGroupTypes.Comparison))
{ {
group.Synchronize = true; group.Synchronize = true;
await SiteGroupService.UpdateSiteGroupAsync(group); await SiteGroupService.UpdateSiteGroupAsync(group);

View File

@@ -53,7 +53,7 @@ namespace Oqtane.Infrastructure
var siteGroups = siteGroupRepository.GetSiteGroups(); var siteGroups = siteGroupRepository.GetSiteGroups();
// iterate through site groups which need to be synchronized // iterate through site groups which need to be synchronized
foreach (var siteGroup in siteGroups.Where(item => item.Type == SiteGroupTypes.Synchronization && item.Synchronize)) foreach (var siteGroup in siteGroups.Where(item => item.Synchronize && (item.Type == SiteGroupTypes.Synchronization || item.Type == SiteGroupTypes.Comparison)))
{ {
// get data // get data
if (siteGroupMembers == null) if (siteGroupMembers == null)
@@ -64,7 +64,8 @@ namespace Oqtane.Infrastructure
} }
var aliasName = "https://" + aliases.First(item => item.TenantId == tenantManager.GetTenant().TenantId && item.SiteId == siteGroup.PrimarySiteId && item.IsDefault).Name; var aliasName = "https://" + aliases.First(item => item.TenantId == tenantManager.GetTenant().TenantId && item.SiteId == siteGroup.PrimarySiteId && item.IsDefault).Name;
log += $"Processing Primary Site: {sites.First(item => item.SiteId == siteGroup.PrimarySiteId).Name} - {CreateLink(aliasName)}<br />"; log += (siteGroup.Type == SiteGroupTypes.Synchronization) ? "Synchronizing " : "Comparing ";
log += $"Primary Site: {sites.First(item => item.SiteId == siteGroup.PrimarySiteId).Name} - {CreateLink(aliasName)}<br />";
// get primary site // get primary site
var primarySite = sites.FirstOrDefault(item => item.SiteId == siteGroup.PrimarySiteId); var primarySite = sites.FirstOrDefault(item => item.SiteId == siteGroup.PrimarySiteId);
@@ -90,14 +91,14 @@ namespace Oqtane.Infrastructure
siteGroupMember.SynchronizedOn = DateTime.MinValue; siteGroupMember.SynchronizedOn = DateTime.MinValue;
} }
// replicate site // synchronize site
var siteLog = ReplicateSite(provider, tenantManager, settingRepository, siteGroupMember, primarySite, secondarySite); var siteLog = SynchronizeSite(provider, tenantManager, settingRepository, siteGroupMember, primarySite, secondarySite);
// set synchronized on date/time // set synchronized on date/time
siteGroupMember.SynchronizedOn = DateTime.UtcNow; siteGroupMember.SynchronizedOn = DateTime.UtcNow;
siteGroupMemberRepository.UpdateSiteGroupMember(siteGroupMember); siteGroupMemberRepository.UpdateSiteGroupMember(siteGroupMember);
log += $"Processed Secondary Site: {secondarySite.Name} - {CreateLink(siteGroupMember.AliasName)}<br />" + siteLog; log += $"With Secondary Site: {secondarySite.Name} - {CreateLink(siteGroupMember.AliasName)}<br />" + siteLog;
} }
else else
{ {
@@ -119,20 +120,20 @@ namespace Oqtane.Infrastructure
return log; return log;
} }
private string ReplicateSite(IServiceProvider provider, ITenantManager tenantManager, ISettingRepository settingRepository, SiteGroupMember siteGroupMember, Site primarySite, Site secondarySite) private string SynchronizeSite(IServiceProvider provider, ITenantManager tenantManager, ISettingRepository settingRepository, SiteGroupMember siteGroupMember, Site primarySite, Site secondarySite)
{ {
var log = ""; var log = "";
// replicate roles/users // synchronize roles/users
log += ReplicateRoles(provider, settingRepository, siteGroupMember, primarySite.SiteId, secondarySite.SiteId); log += SynchronizeRoles(provider, settingRepository, siteGroupMember, primarySite.SiteId, secondarySite.SiteId);
// replicate folders/files // synchronize folders/files
log += ReplicateFolders(provider, settingRepository, siteGroupMember, primarySite.SiteId, secondarySite.SiteId); log += SynchronizeFolders(provider, settingRepository, siteGroupMember, primarySite.SiteId, secondarySite.SiteId);
// replicate pages/modules // synchronize pages/modules
log += ReplicatePages(provider, settingRepository, tenantManager, siteGroupMember, primarySite.SiteId, secondarySite.SiteId); log += SynchronizePages(provider, settingRepository, tenantManager, siteGroupMember, primarySite.SiteId, secondarySite.SiteId);
// replicate site // synchronize site
if (primarySite.ModifiedOn > siteGroupMember.SynchronizedOn) if (primarySite.ModifiedOn > siteGroupMember.SynchronizedOn)
{ {
secondarySite.TimeZoneId = primarySite.TimeZoneId; secondarySite.TimeZoneId = primarySite.TimeZoneId;
@@ -177,23 +178,26 @@ namespace Oqtane.Infrastructure
secondarySite.DeletedBy = primarySite.DeletedBy; secondarySite.DeletedBy = primarySite.DeletedBy;
secondarySite.DeletedOn = primarySite.DeletedOn; secondarySite.DeletedOn = primarySite.DeletedOn;
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{
var siteRepository = provider.GetRequiredService<ISiteRepository>(); var siteRepository = provider.GetRequiredService<ISiteRepository>();
siteRepository.UpdateSite(secondarySite); siteRepository.UpdateSite(secondarySite);
}
log += Log(siteGroupMember, $"Site Updated: {secondarySite.Name}"); log += Log(siteGroupMember, $"Site Updated: {secondarySite.Name}");
} }
// site settings // site settings
log += ReplicateSettings(settingRepository, siteGroupMember, EntityNames.Site, primarySite.SiteId, secondarySite.SiteId); log += SynchronizeSettings(settingRepository, siteGroupMember, EntityNames.Site, primarySite.SiteId, secondarySite.SiteId);
if (siteGroupMember.SynchronizedOn == DateTime.MinValue || !string.IsNullOrEmpty(log)) if (siteGroupMember.SynchronizedOn == DateTime.MinValue || !string.IsNullOrEmpty(log))
{ {
// clear cache for secondary site if any content was replicated // clear cache for secondary site if any content was Synchronized
var syncManager = provider.GetRequiredService<ISyncManager>(); var syncManager = provider.GetRequiredService<ISyncManager>();
var alias = new Alias { TenantId = tenantManager.GetTenant().TenantId, SiteId = secondarySite.SiteId }; var alias = new Alias { TenantId = tenantManager.GetTenant().TenantId, SiteId = secondarySite.SiteId };
syncManager.AddSyncEvent(alias, EntityNames.Site, secondarySite.SiteId, SyncEventActions.Refresh); syncManager.AddSyncEvent(alias, EntityNames.Site, secondarySite.SiteId, SyncEventActions.Refresh);
} }
if (!string.IsNullOrEmpty(log) && siteGroupMember.Notify) if (!string.IsNullOrEmpty(log) && siteGroupMember.SiteGroup.Type == SiteGroupTypes.Comparison)
{ {
// send change log to administrators // send change log to administrators
SendNotifications(provider, secondarySite.SiteId, secondarySite.Name, log); SendNotifications(provider, secondarySite.SiteId, secondarySite.Name, log);
@@ -213,7 +217,7 @@ namespace Oqtane.Infrastructure
return fileId; return fileId;
} }
private string ReplicateRoles(IServiceProvider provider, ISettingRepository settingRepository, SiteGroupMember siteGroupMember, int primarySiteId, int secondarySiteId) private string SynchronizeRoles(IServiceProvider provider, ISettingRepository settingRepository, SiteGroupMember siteGroupMember, int primarySiteId, int secondarySiteId)
{ {
// get roles // get roles
var roleRepository = provider.GetRequiredService<IRoleRepository>(); var roleRepository = provider.GetRequiredService<IRoleRepository>();
@@ -241,13 +245,19 @@ namespace Oqtane.Infrastructure
secondaryRole.IsSystem = primaryRole.IsSystem; secondaryRole.IsSystem = primaryRole.IsSystem;
if (role == null) if (role == null)
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
roleRepository.AddRole(secondaryRole); roleRepository.AddRole(secondaryRole);
}
log += Log(siteGroupMember, $"Role Added: {secondaryRole.Name}"); log += Log(siteGroupMember, $"Role Added: {secondaryRole.Name}");
} }
else else
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
roleRepository.UpdateRole(secondaryRole); roleRepository.UpdateRole(secondaryRole);
}
log += Log(siteGroupMember, $"Role Updated: {secondaryRole.Name}"); log += Log(siteGroupMember, $"Role Updated: {secondaryRole.Name}");
secondaryRoles.Remove(role); secondaryRoles.Remove(role);
} }
@@ -256,18 +266,21 @@ namespace Oqtane.Infrastructure
// remove roles in the secondary site which do not exist in the primary site // remove roles in the secondary site which do not exist in the primary site
foreach (var secondaryRole in secondaryRoles.Where(item => !primaryRoles.Select(item => item.Name).Contains(item.Name))) foreach (var secondaryRole in secondaryRoles.Where(item => !primaryRoles.Select(item => item.Name).Contains(item.Name)))
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
roleRepository.DeleteRole(secondaryRole.RoleId); roleRepository.DeleteRole(secondaryRole.RoleId);
}
log += Log(siteGroupMember, $"Role Deleted: {secondaryRole.Name}"); log += Log(siteGroupMember, $"Role Deleted: {secondaryRole.Name}");
} }
// settings // settings
log += ReplicateSettings(settingRepository, siteGroupMember, EntityNames.Role, primarySiteId, secondarySiteId); log += SynchronizeSettings(settingRepository, siteGroupMember, EntityNames.Role, primarySiteId, secondarySiteId);
return log; return log;
} }
private string ReplicateFolders(IServiceProvider provider, ISettingRepository settingRepository, SiteGroupMember siteGroupMember, int primarySiteId, int secondarySiteId) private string SynchronizeFolders(IServiceProvider provider, ISettingRepository settingRepository, SiteGroupMember siteGroupMember, int primarySiteId, int secondarySiteId)
{ {
var folderRepository = provider.GetRequiredService<IFolderRepository>(); var folderRepository = provider.GetRequiredService<IFolderRepository>();
var fileRepository = provider.GetRequiredService<IFileRepository>(); var fileRepository = provider.GetRequiredService<IFileRepository>();
@@ -308,23 +321,29 @@ namespace Oqtane.Infrastructure
secondaryFolder.Capacity = primaryFolder.Capacity; secondaryFolder.Capacity = primaryFolder.Capacity;
secondaryFolder.ImageSizes = primaryFolder.ImageSizes; secondaryFolder.ImageSizes = primaryFolder.ImageSizes;
secondaryFolder.IsSystem = primaryFolder.IsSystem; secondaryFolder.IsSystem = primaryFolder.IsSystem;
secondaryFolder.PermissionList = ReplicatePermissions(primaryFolder.PermissionList, secondarySiteId); secondaryFolder.PermissionList = SynchronizePermissions(primaryFolder.PermissionList, secondarySiteId);
if (folder == null) if (folder == null)
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
folderRepository.AddFolder(secondaryFolder); folderRepository.AddFolder(secondaryFolder);
}
log += Log(siteGroupMember, $"Folder Added: {secondaryFolder.Path}"); log += Log(siteGroupMember, $"Folder Added: {secondaryFolder.Path}");
} }
else else
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
folderRepository.UpdateFolder(secondaryFolder); folderRepository.UpdateFolder(secondaryFolder);
}
log += Log(siteGroupMember, $"Folder Updated: {secondaryFolder.Path}"); log += Log(siteGroupMember, $"Folder Updated: {secondaryFolder.Path}");
secondaryFolders.Remove(folder); secondaryFolders.Remove(folder);
} }
} }
// folder settings // folder settings
log += ReplicateSettings(settingRepository, siteGroupMember, EntityNames.Folder, primaryFolder.FolderId, secondaryFolder.FolderId); log += SynchronizeSettings(settingRepository, siteGroupMember, EntityNames.Folder, primaryFolder.FolderId, secondaryFolder.FolderId);
// get files for folder // get files for folder
var primaryFiles = fileRepository.GetFiles(primaryFolder.FolderId); var primaryFiles = fileRepository.GetFiles(primaryFolder.FolderId);
@@ -352,15 +371,21 @@ namespace Oqtane.Infrastructure
secondaryFile.Description = primaryFile.Description; secondaryFile.Description = primaryFile.Description;
if (file == null) if (file == null)
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
fileRepository.AddFile(secondaryFile); fileRepository.AddFile(secondaryFile);
ReplicateFile(folderRepository, primaryFolder, primaryFile, secondaryFolder, secondaryFile); SynchronizeFile(folderRepository, primaryFolder, primaryFile, secondaryFolder, secondaryFile);
}
log += Log(siteGroupMember, $"File Added: {CreateLink(siteGroupMember.AliasName + secondaryFolder.Path + secondaryFile.Name)}"); log += Log(siteGroupMember, $"File Added: {CreateLink(siteGroupMember.AliasName + secondaryFolder.Path + secondaryFile.Name)}");
} }
else else
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
fileRepository.UpdateFile(secondaryFile); fileRepository.UpdateFile(secondaryFile);
ReplicateFile(folderRepository, primaryFolder, primaryFile, secondaryFolder, secondaryFile); SynchronizeFile(folderRepository, primaryFolder, primaryFile, secondaryFolder, secondaryFile);
}
log += Log(siteGroupMember, $"File Updated: {CreateLink(siteGroupMember.AliasName + secondaryFolder.Path + secondaryFile.Name)}"); log += Log(siteGroupMember, $"File Updated: {CreateLink(siteGroupMember.AliasName + secondaryFolder.Path + secondaryFile.Name)}");
secondaryFiles.Remove(file); secondaryFiles.Remove(file);
} }
@@ -369,25 +394,31 @@ namespace Oqtane.Infrastructure
// remove files in the secondary site which do not exist in the primary site // remove files in the secondary site which do not exist in the primary site
foreach (var secondaryFile in secondaryFiles.Where(item => !primaryFiles.Select(item => item.Name).Contains(item.Name))) foreach (var secondaryFile in secondaryFiles.Where(item => !primaryFiles.Select(item => item.Name).Contains(item.Name)))
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
fileRepository.DeleteFile(secondaryFile.FileId); fileRepository.DeleteFile(secondaryFile.FileId);
var secondaryPath = Path.Combine(folderRepository.GetFolderPath(secondaryFolder), secondaryFile.Name); var secondaryPath = Path.Combine(folderRepository.GetFolderPath(secondaryFolder), secondaryFile.Name);
System.IO.File.Delete(secondaryPath); System.IO.File.Delete(secondaryPath);
}
log += Log(siteGroupMember, $"File Deleted: {CreateLink(siteGroupMember.AliasName + secondaryFolder.Path + secondaryFile.Name)}"); log += Log(siteGroupMember, $"File Deleted: {CreateLink(siteGroupMember.AliasName + secondaryFolder.Path + secondaryFile.Name)}");
} }
} }
// remove folders in the secondary site which do not exist in the primary site // remove folders in the secondary site which do not exist in the primary site
foreach (var secondaryFolder in secondaryFolders.Where(item => !primaryFolders.Select(item => item.Path).Contains(item.Path))) foreach (var secondaryFolder in secondaryFolders.Where(item => !primaryFolders.Select(item => item.Path).Contains(item.Path)))
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
folderRepository.DeleteFolder(secondaryFolder.FolderId); folderRepository.DeleteFolder(secondaryFolder.FolderId);
}
log += Log(siteGroupMember, $"Folder Deleted: {secondaryFolder.Path}"); log += Log(siteGroupMember, $"Folder Deleted: {secondaryFolder.Path}");
} }
return log; return log;
} }
private void ReplicateFile(IFolderRepository folderRepository, Folder primaryFolder, Models.File primaryFile, Folder secondaryFolder, Models.File secondaryFile) private void SynchronizeFile(IFolderRepository folderRepository, Folder primaryFolder, Models.File primaryFile, Folder secondaryFolder, Models.File secondaryFile)
{ {
var primaryPath = Path.Combine(folderRepository.GetFolderPath(primaryFolder), primaryFile.Name); var primaryPath = Path.Combine(folderRepository.GetFolderPath(primaryFolder), primaryFile.Name);
if (System.IO.File.Exists(primaryPath)) if (System.IO.File.Exists(primaryPath))
@@ -401,7 +432,7 @@ namespace Oqtane.Infrastructure
} }
} }
private string ReplicatePages(IServiceProvider provider, ISettingRepository settingRepository, ITenantManager tenantManager, SiteGroupMember siteGroupMember, int primarySiteId, int secondarySiteId) private string SynchronizePages(IServiceProvider provider, ISettingRepository settingRepository, ITenantManager tenantManager, SiteGroupMember siteGroupMember, int primarySiteId, int secondarySiteId)
{ {
var pageRepository = provider.GetRequiredService<IPageRepository>(); var pageRepository = provider.GetRequiredService<IPageRepository>();
var pageModuleRepository = provider.GetRequiredService<IPageModuleRepository>(); var pageModuleRepository = provider.GetRequiredService<IPageModuleRepository>();
@@ -464,23 +495,29 @@ namespace Oqtane.Infrastructure
secondaryPage.DeletedBy = primaryPage.DeletedBy; secondaryPage.DeletedBy = primaryPage.DeletedBy;
secondaryPage.DeletedOn = primaryPage.DeletedOn; secondaryPage.DeletedOn = primaryPage.DeletedOn;
secondaryPage.IsDeleted = primaryPage.IsDeleted; secondaryPage.IsDeleted = primaryPage.IsDeleted;
secondaryPage.PermissionList = ReplicatePermissions(primaryPage.PermissionList, secondarySiteId); secondaryPage.PermissionList = SynchronizePermissions(primaryPage.PermissionList, secondarySiteId);
if (page == null) if (page == null)
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
secondaryPage = pageRepository.AddPage(secondaryPage); secondaryPage = pageRepository.AddPage(secondaryPage);
}
log += Log(siteGroupMember, $"Page Added: {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}"); log += Log(siteGroupMember, $"Page Added: {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}");
} }
else else
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
secondaryPage = pageRepository.UpdatePage(secondaryPage); secondaryPage = pageRepository.UpdatePage(secondaryPage);
}
log += Log(siteGroupMember, $"Page Updated: {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}"); log += Log(siteGroupMember, $"Page Updated: {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}");
secondaryPages.Remove(page); secondaryPages.Remove(page);
} }
} }
// page settings // page settings
log += ReplicateSettings(settingRepository, siteGroupMember, EntityNames.Page, primaryPage.PageId, secondaryPage.PageId); log += SynchronizeSettings(settingRepository, siteGroupMember, EntityNames.Page, primaryPage.PageId, secondaryPage.PageId);
// modules // modules
if (primaryPageModules == null) if (primaryPageModules == null)
@@ -517,7 +554,7 @@ namespace Oqtane.Infrastructure
secondaryPageModule.Header = primaryPageModule.Header; secondaryPageModule.Header = primaryPageModule.Header;
secondaryPageModule.Footer = primaryPageModule.Footer; secondaryPageModule.Footer = primaryPageModule.Footer;
secondaryPageModule.IsDeleted = primaryPageModule.IsDeleted; secondaryPageModule.IsDeleted = primaryPageModule.IsDeleted;
secondaryPageModule.Module.PermissionList = ReplicatePermissions(primaryPageModule.Module.PermissionList, secondarySiteId); secondaryPageModule.Module.PermissionList = SynchronizePermissions(primaryPageModule.Module.PermissionList, secondarySiteId);
secondaryPageModule.Module.AllPages = false; secondaryPageModule.Module.AllPages = false;
secondaryPageModule.Module.IsDeleted = false; secondaryPageModule.Module.IsDeleted = false;
@@ -530,15 +567,21 @@ namespace Oqtane.Infrastructure
if (module == null) if (module == null)
{ {
// add new module // add new module
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{
module = moduleRepository.AddModule(secondaryPageModule.Module); module = moduleRepository.AddModule(secondaryPageModule.Module);
updateContent = true; updateContent = true;
}
log += Log(siteGroupMember, $"Module Added: {module.Title} - {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}"); log += Log(siteGroupMember, $"Module Added: {module.Title} - {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}");
} }
if (module != null) if (module != null)
{ {
secondaryPageModule.ModuleId = module.ModuleId; secondaryPageModule.ModuleId = module.ModuleId;
secondaryPageModule.Module = null; // remove tracking secondaryPageModule.Module = null; // remove tracking
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{
secondaryPageModule = pageModuleRepository.AddPageModule(secondaryPageModule); secondaryPageModule = pageModuleRepository.AddPageModule(secondaryPageModule);
}
log += Log(siteGroupMember, $"Module Instance Added: {module.Title} - {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}"); log += Log(siteGroupMember, $"Module Instance Added: {module.Title} - {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}");
secondaryPageModule.Module = module; secondaryPageModule.Module = module;
} }
@@ -547,14 +590,20 @@ namespace Oqtane.Infrastructure
{ {
// update existing module // update existing module
if (primaryPageModule.Module.ModifiedOn > siteGroupMember.SynchronizedOn) if (primaryPageModule.Module.ModifiedOn > siteGroupMember.SynchronizedOn)
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
moduleRepository.UpdateModule(secondaryPageModule.Module); moduleRepository.UpdateModule(secondaryPageModule.Module);
}
updateContent = true; updateContent = true;
log += Log(siteGroupMember, $"Module Updated: {secondaryPageModule.Title} - {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}"); log += Log(siteGroupMember, $"Module Updated: {secondaryPageModule.Title} - {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}");
} }
if (primaryPageModule.ModifiedOn > siteGroupMember.SynchronizedOn) if (primaryPageModule.ModifiedOn > siteGroupMember.SynchronizedOn)
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
secondaryPageModule = pageModuleRepository.UpdatePageModule(secondaryPageModule); secondaryPageModule = pageModuleRepository.UpdatePageModule(secondaryPageModule);
}
log += Log(siteGroupMember, $"Module Instance Updated: {secondaryPageModule.Title} - {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}"); log += Log(siteGroupMember, $"Module Instance Updated: {secondaryPageModule.Title} - {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}");
secondaryPageModules.Remove(pageModule); secondaryPageModules.Remove(pageModule);
} }
@@ -572,8 +621,11 @@ namespace Oqtane.Infrastructure
var primaryModuleContent = ((ISynchronizable)moduleObject).ExtractModule(primaryPageModule.Module); var primaryModuleContent = ((ISynchronizable)moduleObject).ExtractModule(primaryPageModule.Module);
var secondaryModuleContent = ((ISynchronizable)moduleObject).ExtractModule(secondaryPageModule.Module); var secondaryModuleContent = ((ISynchronizable)moduleObject).ExtractModule(secondaryPageModule.Module);
if (primaryModuleContent != secondaryModuleContent) if (primaryModuleContent != secondaryModuleContent)
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
((ISynchronizable)moduleObject).LoadModule(secondaryPageModule.Module, primaryModuleContent, primaryPageModule.Module.ModuleDefinition.Version); ((ISynchronizable)moduleObject).LoadModule(secondaryPageModule.Module, primaryModuleContent, primaryPageModule.Module.ModuleDefinition.Version);
}
log += Log(siteGroupMember, $"Module Content Updated: {secondaryPageModule.Title} - {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}"); log += Log(siteGroupMember, $"Module Content Updated: {secondaryPageModule.Title} - {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}");
} }
} }
@@ -586,7 +638,7 @@ namespace Oqtane.Infrastructure
} }
// module settings // module settings
log += ReplicateSettings(settingRepository, siteGroupMember, EntityNames.Module, primaryPageModule.ModuleId, secondaryPageModule.ModuleId); log += SynchronizeSettings(settingRepository, siteGroupMember, EntityNames.Module, primaryPageModule.ModuleId, secondaryPageModule.ModuleId);
} }
} }
@@ -604,22 +656,28 @@ namespace Oqtane.Infrastructure
} }
} }
if (!primaryPageModules.Any(item => item.PageId == primaryPageId && item.Module.ModuleDefinitionName == secondaryPageModule.Module.ModuleDefinitionName && item.Title.ToLower() == secondaryPageModule.Title.ToLower())) if (!primaryPageModules.Any(item => item.PageId == primaryPageId && item.Module.ModuleDefinitionName == secondaryPageModule.Module.ModuleDefinitionName && item.Title.ToLower() == secondaryPageModule.Title.ToLower()))
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
pageModuleRepository.DeletePageModule(secondaryPageModule.PageModuleId); pageModuleRepository.DeletePageModule(secondaryPageModule.PageModuleId);
}
log += Log(siteGroupMember, $"Module Instance Deleted: {secondaryPageModule.Title} - {CreateLink(siteGroupMember.AliasName + secondaryPageModule.Page.Path)}"); log += Log(siteGroupMember, $"Module Instance Deleted: {secondaryPageModule.Title} - {CreateLink(siteGroupMember.AliasName + secondaryPageModule.Page.Path)}");
} }
} }
// remove pages in the secondary site which do not exist in the primary site // remove pages in the secondary site which do not exist in the primary site
foreach (var secondaryPage in secondaryPages.Where(item => !primaryPages.Select(item => item.Path).Contains(item.Path))) foreach (var secondaryPage in secondaryPages.Where(item => !primaryPages.Select(item => item.Path).Contains(item.Path)))
{
if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization)
{ {
pageRepository.DeletePage(secondaryPage.PageId); pageRepository.DeletePage(secondaryPage.PageId);
}
log += Log(siteGroupMember, $"Page Deleted: {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}"); log += Log(siteGroupMember, $"Page Deleted: {CreateLink(siteGroupMember.AliasName + secondaryPage.Path)}");
} }
if (siteGroupMember.SynchronizedOn == DateTime.MinValue || !string.IsNullOrEmpty(log)) if (siteGroupMember.SynchronizedOn == DateTime.MinValue || !string.IsNullOrEmpty(log))
{ {
// clear cache for secondary site if any content was replicated // clear cache for secondary site if any content was Synchronized
var syncManager = provider.GetRequiredService<ISyncManager>(); var syncManager = provider.GetRequiredService<ISyncManager>();
var alias = new Alias { TenantId = tenantManager.GetTenant().TenantId, SiteId = secondarySiteId }; var alias = new Alias { TenantId = tenantManager.GetTenant().TenantId, SiteId = secondarySiteId };
syncManager.AddSyncEvent(alias, EntityNames.Site, secondarySiteId, SyncEventActions.Refresh); syncManager.AddSyncEvent(alias, EntityNames.Site, secondarySiteId, SyncEventActions.Refresh);
@@ -628,7 +686,7 @@ namespace Oqtane.Infrastructure
return log; return log;
} }
private List<Permission> ReplicatePermissions(List<Permission> permissionList, int siteId) private List<Permission> SynchronizePermissions(List<Permission> permissionList, int siteId)
{ {
return permissionList.Select(item => new Permission return permissionList.Select(item => new Permission
{ {
@@ -644,7 +702,7 @@ namespace Oqtane.Infrastructure
}).ToList(); }).ToList();
} }
private string ReplicateSettings(ISettingRepository settingRepository, SiteGroupMember siteGroupMember, string entityName, int primaryEntityId, int secondaryEntityId) private string SynchronizeSettings(ISettingRepository settingRepository, SiteGroupMember siteGroupMember, string entityName, int primaryEntityId, int secondaryEntityId)
{ {
var log = ""; var log = "";
var updated = false; var updated = false;
@@ -661,7 +719,7 @@ namespace Oqtane.Infrastructure
secondarySetting.SettingName = primarySetting.SettingName; secondarySetting.SettingName = primarySetting.SettingName;
secondarySetting.SettingValue = primarySetting.SettingValue; secondarySetting.SettingValue = primarySetting.SettingValue;
secondarySetting.IsPrivate = primarySetting.IsPrivate; secondarySetting.IsPrivate = primarySetting.IsPrivate;
if (!excludedSettings.Any(item => item.EntityName == secondarySetting.EntityName && item.SettingName == secondarySetting.SettingName)) if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization && !excludedSettings.Any(item => item.EntityName == secondarySetting.EntityName && item.SettingName == secondarySetting.SettingName))
{ {
settingRepository.AddSetting(secondarySetting); settingRepository.AddSetting(secondarySetting);
updated = true; updated = true;
@@ -673,7 +731,7 @@ namespace Oqtane.Infrastructure
{ {
secondarySetting.SettingValue = primarySetting.SettingValue; secondarySetting.SettingValue = primarySetting.SettingValue;
secondarySetting.IsPrivate = primarySetting.IsPrivate; secondarySetting.IsPrivate = primarySetting.IsPrivate;
if (!excludedSettings.Any(item => item.EntityName == secondarySetting.EntityName && item.SettingName == secondarySetting.SettingName)) if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization && !excludedSettings.Any(item => item.EntityName == secondarySetting.EntityName && item.SettingName == secondarySetting.SettingName))
{ {
settingRepository.UpdateSetting(secondarySetting); settingRepository.UpdateSetting(secondarySetting);
updated = true; updated = true;
@@ -686,7 +744,7 @@ namespace Oqtane.Infrastructure
// any remaining secondary settings need to be deleted // any remaining secondary settings need to be deleted
foreach (var secondarySetting in secondarySettings) foreach (var secondarySetting in secondarySettings)
{ {
if (!excludedSettings.Any(item => item.EntityName == secondarySetting.EntityName && item.SettingName == secondarySetting.SettingName)) if (siteGroupMember.SiteGroup.Type == SiteGroupTypes.Synchronization && !excludedSettings.Any(item => item.EntityName == secondarySetting.EntityName && item.SettingName == secondarySetting.SettingName))
{ {
settingRepository.DeleteSetting(secondarySetting.EntityName, secondarySetting.SettingId); settingRepository.DeleteSetting(secondarySetting.EntityName, secondarySetting.SettingId);
updated = true; updated = true;
@@ -715,7 +773,7 @@ namespace Oqtane.Infrastructure
private string Log(SiteGroupMember siteGroupMember, string content) private string Log(SiteGroupMember siteGroupMember, string content)
{ {
// not necessary to log initial replication // not necessary to log initial synchronization
if (siteGroupMember.SynchronizedOn != DateTime.MinValue) if (siteGroupMember.SynchronizedOn != DateTime.MinValue)
{ {
return content + "<br />"; return content + "<br />";

View File

@@ -28,7 +28,6 @@ namespace Oqtane.Migrations.EntityBuilders
SiteGroupMemberId = AddAutoIncrementColumn(table, "SiteGroupMemberId"); SiteGroupMemberId = AddAutoIncrementColumn(table, "SiteGroupMemberId");
SiteGroupId = AddIntegerColumn(table, "SiteGroupId"); SiteGroupId = AddIntegerColumn(table, "SiteGroupId");
SiteId = AddIntegerColumn(table, "SiteId"); SiteId = AddIntegerColumn(table, "SiteId");
Notify = AddBooleanColumn(table, "Notify");
SynchronizedOn = AddDateTimeColumn(table, "SynchronizedOn", true); SynchronizedOn = AddDateTimeColumn(table, "SynchronizedOn", true);
AddAuditableColumns(table); AddAuditableColumns(table);
@@ -42,8 +41,6 @@ namespace Oqtane.Migrations.EntityBuilders
public OperationBuilder<AddColumnOperation> SiteId { get; set; } public OperationBuilder<AddColumnOperation> SiteId { get; set; }
public OperationBuilder<AddColumnOperation> Notify { get; set; }
public OperationBuilder<AddColumnOperation> SynchronizedOn { get; set; } public OperationBuilder<AddColumnOperation> SynchronizedOn { get; set; }
} }
} }

View File

@@ -20,11 +20,6 @@ namespace Oqtane.Models
/// </summary> /// </summary>
public int SiteId { get; set; } public int SiteId { get; set; }
/// <summary>
/// Indicates if the site administrator should be notified of any synchronization activity
/// </summary>
public bool Notify { get; set; }
/// <summary> /// <summary>
/// The last date/time the site was synchronized /// The last date/time the site was synchronized
/// </summary> /// </summary>

View File

@@ -3,6 +3,7 @@ namespace Oqtane.Shared
public class SiteGroupTypes public class SiteGroupTypes
{ {
public const string Synchronization = "Synchronization"; public const string Synchronization = "Synchronization";
public const string Comparison = "Comparison";
public const string Localization = "Localization"; public const string Localization = "Localization";
} }
} }