From 5c0afc3ab4870641c518db5d1f05fec7a22400d7 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Mon, 13 Oct 2025 13:31:48 +0100 Subject: [PATCH 01/28] Production 'mode' where we merge all the files together. --- .../appsettings-schema.usync.json | 30 +++++++++++- .../Configuration/SyncConfigService.cs | 19 ++++--- .../Configuration/uSyncSettings.cs | 45 +++++++++++------ uSync.BackOffice/Services/ISyncFileService.cs | 5 ++ uSync.BackOffice/Services/ISyncService.cs | 6 +++ uSync.BackOffice/Services/SyncFileService.cs | 23 ++++++++- .../Services/SyncService_Files.cs | 21 ++++++++ .../Services/SyncService_Handlers.cs | 24 ++++++++- .../SyncHandlers/Handlers/DataTypeHandler.cs | 36 +------------- .../SyncHandlers/Interfaces/ISyncHandler.cs | 4 ++ .../SyncHandlers/SyncHandlerContainerBase.cs | 15 +++++- .../SyncHandlers/SyncHandlerRoot.cs | 49 +++++++++++++++++-- .../uSyncBackOfficeBuilderExtensions.cs | 3 ++ .../Actions/SyncFolderController.cs | 37 ++++++++++++++ .../Actions/uSyncActionsController.cs | 2 +- .../usync-assets/package-lock.json | 4 +- .../usync-assets/package.json | 2 +- 17 files changed, 256 insertions(+), 69 deletions(-) create mode 100644 uSync.Backoffice.Management.Api/Controllers/Actions/SyncFolderController.cs diff --git a/uSync.BackOffice.Targets/appsettings-schema.usync.json b/uSync.BackOffice.Targets/appsettings-schema.usync.json index e539eee12..576d48c7d 100644 --- a/uSync.BackOffice.Targets/appsettings-schema.usync.json +++ b/uSync.BackOffice.Targets/appsettings-schema.usync.json @@ -205,7 +205,7 @@ }, "DisableNotificationSuppression": { "type": "boolean", - "description": "turns of use of the Notifications.Supress method, so notifications\nfire after every item is imported.\n ", + "description": "turns of use of the Notifications.Suppress method, so notifications\nfire after every item is imported.\n ", "default": "true" }, "BackgroundNotifications": { @@ -217,9 +217,37 @@ "type": "boolean", "description": "Move the uSync tree to it's own section in the back office. \n(requires a restart to take effect).\n ", "default": false + }, + "FolderMode": { + "description": "What type of mode the folder should work in (default, root, or production)\n ", + "default": "Normal", + "oneOf": [ + { + "$ref": "#/definitions/USyncBackOfficeConfigurationSyncFolderMode" + } + ] + }, + "ProductionFolder": { + "type": "string", + "description": "location of the 'production' folder to use when in production mode, \nor when creating the production mode files.\n ", + "default": "uSync/production" } } }, + "USyncBackOfficeConfigurationSyncFolderMode": { + "type": "string", + "description": "", + "x-enumNames": [ + "Normal", + "Root", + "Production" + ], + "enum": [ + "Normal", + "Root", + "Production" + ] + }, "USyncuSyncSetsDefinition": { "type": "object", "properties": { diff --git a/uSync.BackOffice/Configuration/SyncConfigService.cs b/uSync.BackOffice/Configuration/SyncConfigService.cs index a142000ad..ec0c5ad95 100644 --- a/uSync.BackOffice/Configuration/SyncConfigService.cs +++ b/uSync.BackOffice/Configuration/SyncConfigService.cs @@ -66,11 +66,8 @@ private class SyncFolderItem /// public string GetWorkingFolder() { - var folders = FetchFolders(); - - return Settings.IsRootSite - ? folders[0].TrimStart('/') - : folders.Last().TrimStart('/'); + var folders = GetFolders(); + return folders.Last().TrimStart('/'); } /// @@ -78,9 +75,15 @@ public string[] GetFolders() { var folders = FetchFolders(); - return Settings.IsRootSite - ? [folders[0].TrimStart('/')] - : [.. folders.Select(x => x.TrimStart('/'))]; + switch(Settings.FolderMode) + { + case SyncFolderMode.Root: + return [folders[0].TrimStart('/')]; + case SyncFolderMode.Production: + return [Settings.ProductionFolder]; + default: + return [.. folders.Select(x => x.TrimStart('/'))]; + } } /// diff --git a/uSync.BackOffice/Configuration/uSyncSettings.cs b/uSync.BackOffice/Configuration/uSyncSettings.cs index c290fe195..6d1a2ca17 100644 --- a/uSync.BackOffice/Configuration/uSyncSettings.cs +++ b/uSync.BackOffice/Configuration/uSyncSettings.cs @@ -197,26 +197,22 @@ public class uSyncSettings public string HideAddOns { get; set; } = "licence"; /// - /// turns of use of the Notifications.Supress method, so notifications + /// turns of use of the Notifications.Suppress method, so notifications /// fire after every item is imported. /// /// - /// I am not sure this does what i think it does, it doesn't suppress - /// then fire at the end , it just suppresses them all. + /// this disables the internal uSync scope provider that delays all + /// non cancellable notifications until after the import is complete. /// - /// until we have had time to look at this , we will leave this as - /// disabled by default so all notification messages fire. + /// on v13 this is false, the import happens and then the notifications fire. /// - /// for v13 thius is fine, but for v14, grouping the notifications - /// can causes issues if something fails. - /// - /// So if a single content import fails then the whole batch doesn't - /// get published properly (so no content for you :( ) . + /// on v16 the default is true, because some of the notifications appear to + /// be closely coupled to the save/publish process, and if something goes + /// wrong in one item's import it can cause a cascade of failures across + /// everything that might have been imported along with it. /// - /// there might be something downlever we can do, but it likey means - /// lots of core investigation to find that, for now 'old' school - /// non suppressed notifications should be fine (if a little slower). - /// + /// if the notifications are not suppressed, then if an item fails to import + /// it doesn't stop other items from being imported. /// [DefaultValue("true")] public bool DisableNotificationSuppression { get; set; } = true; @@ -238,4 +234,25 @@ public class uSyncSettings /// [DefaultValue(false)] public bool MoveToSection { get; set; } = false; + + /// + /// What type of mode the folder should work in (default, root, or production) + /// + [DefaultValue("Normal")] + public SyncFolderMode FolderMode { get; set; } = SyncFolderMode.Normal; + + /// + /// location of the 'production' folder to use when in production mode, + /// or when creating the production mode files. + /// + [DefaultValue("uSync/production")] + public string ProductionFolder { get; set; } = "uSync/production"; } + +public enum SyncFolderMode +{ + Normal, + Root, + Production, +}; + diff --git a/uSync.BackOffice/Services/ISyncFileService.cs b/uSync.BackOffice/Services/ISyncFileService.cs index 40c12c12b..ca4858437 100644 --- a/uSync.BackOffice/Services/ISyncFileService.cs +++ b/uSync.BackOffice/Services/ISyncFileService.cs @@ -135,6 +135,11 @@ public interface ISyncFileService /// Task LoadXElementAsync(string file); + /// + /// merge all the files in the give folders into a single xml node, that can be bulk imported + /// + Task MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string targetFolder, string fileName); + /// /// merge a list of files into a single XElement /// diff --git a/uSync.BackOffice/Services/ISyncService.cs b/uSync.BackOffice/Services/ISyncService.cs index a962c400e..3269faaf3 100644 --- a/uSync.BackOffice/Services/ISyncService.cs +++ b/uSync.BackOffice/Services/ISyncService.cs @@ -156,4 +156,10 @@ public interface ISyncService /// trigger the end of the bulk process /// Task FinishBulkProcessAsync(HandlerActions action, IEnumerable actions); + + /// + /// merge the given folders in single 'production' files for each handler. + /// + Task MergeExportFolder(string[] paths, IEnumerable handlers, bool clean); + } \ No newline at end of file diff --git a/uSync.BackOffice/Services/SyncFileService.cs b/uSync.BackOffice/Services/SyncFileService.cs index bdf7dd3ca..4a4925319 100644 --- a/uSync.BackOffice/Services/SyncFileService.cs +++ b/uSync.BackOffice/Services/SyncFileService.cs @@ -242,6 +242,7 @@ public async Task SaveFileAsync(string filename, string content) { CheckCharacters = false, Async = true, + IgnoreWhitespace = true, }; private static XmlWriterSettings _writerSettings = new XmlWriterSettings @@ -251,8 +252,6 @@ public async Task SaveFileAsync(string filename, string content) Async = true, CloseOutput= false, Indent = true, - - }; /// @@ -323,7 +322,27 @@ public void CopyFolder(string source, string target) { File.Copy(file, file.Replace(resolvedSource, resolvedTarget), true); } + } + + public async Task MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string targetFolder, string filename) + { + var merged = await MergeFoldersAsync(folders, "config", trackerBase); + + var megaNode = new XElement(itemType + "s"); + int count = 0; + foreach(var item in merged) + { + count++; + megaNode.Add(new XElement(item.Node)); + } + + var resolvedTargetFolder = GetAbsPath(targetFolder); + CreateFolder(resolvedTargetFolder); + + var singleFileName = Path.Combine(resolvedTargetFolder, $"{filename}.config"); + await SaveXElementAsync(megaNode, singleFileName); + return count; } /// diff --git a/uSync.BackOffice/Services/SyncService_Files.cs b/uSync.BackOffice/Services/SyncService_Files.cs index d4842ebd5..cbe24b979 100644 --- a/uSync.BackOffice/Services/SyncService_Files.cs +++ b/uSync.BackOffice/Services/SyncService_Files.cs @@ -1,7 +1,11 @@ using System; +using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; +using System.Threading.Tasks; + +using uSync.BackOffice.SyncHandlers.Models; namespace uSync.BackOffice; @@ -104,4 +108,21 @@ private static string CleanPathForZip(string path) => Path.GetFullPath( path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)) .TrimEnd(Path.DirectorySeparatorChar); + + /// + public async Task MergeExportFolder(string[] paths, IEnumerable handlers, bool clean) + { + var totalMerged = 0; + var root = _uSyncConfig.GetWorkingFolder(); + + foreach (var handler in handlers) + { + var folders = paths.Select(x => Path.Combine(x, handler.Handler.DefaultFolder)).ToArray(); + var target = _uSyncConfig.Settings.ProductionFolder; + + totalMerged += await _syncFileService.MakeSingleExportFromFolders(folders, handler.Handler.SerializeType, handler.Handler.BaseTracker, target, handler.Handler.DefaultFolder); + } + + return totalMerged; + } } diff --git a/uSync.BackOffice/Services/SyncService_Handlers.cs b/uSync.BackOffice/Services/SyncService_Handlers.cs index 6d204e05f..350e11e46 100644 --- a/uSync.BackOffice/Services/SyncService_Handlers.cs +++ b/uSync.BackOffice/Services/SyncService_Handlers.cs @@ -38,7 +38,16 @@ public async Task> ReportHandlerAsync(string handler, u if (handlerPair == null) return []; var folders = GetHandlerFolders(GetFolderFromOptions(options), handlerPair.Handler); - return await handlerPair.Handler.ReportAsync(folders, handlerPair.Settings, options.Callbacks?.Update); + if (File.Exists(folders.Last() + ".config")) + { + var fileName = folders.Last() + ".config"; + var node = await _syncFileService.LoadXElementAsync(fileName); + return await handlerPair.Handler.ReportElementAsync(node, fileName, handlerPair.Settings, options); + } + else + { + return await handlerPair.Handler.ReportAsync(folders, handlerPair.Settings, options.Callbacks?.Update); + } } /// > @@ -68,7 +77,18 @@ public async Task> ImportHandlerAsync(string handlerAli backgroundTaskQueue: _backgroundTaskQueue, options.Callbacks?.Update); - var results = await handlerPair.Handler.ImportAllAsync(folders, handlerPair.Settings, options); + List results; + + if (File.Exists(folders.Last() + ".config")) + { + var fileName = folders.Last() + ".config"; + var node = await _syncFileService.LoadXElementAsync(fileName); + results = [.. await handlerPair.Handler.ImportElementAsync(node, fileName, handlerPair.Settings, options)]; + } + else + { + results = [..await handlerPair.Handler.ImportAllAsync(folders, handlerPair.Settings, options)]; + } // _logger.LogDebug("< Import Handler {handler}", handlerAlias); diff --git a/uSync.BackOffice/SyncHandlers/Handlers/DataTypeHandler.cs b/uSync.BackOffice/SyncHandlers/Handlers/DataTypeHandler.cs index b58ae9cd9..6576b964d 100644 --- a/uSync.BackOffice/SyncHandlers/Handlers/DataTypeHandler.cs +++ b/uSync.BackOffice/SyncHandlers/Handlers/DataTypeHandler.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using System.Xml.XPath; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; @@ -66,40 +67,7 @@ public DataTypeHandler( _dataTypeContainerService = dataTypeContainerService; } - /// - /// Process all DataType actions at the end of the import process - /// - /// - /// Datatypes have to exist early on so DocumentTypes can reference them, but - /// some doctypes reference content or document types, so we re-process them - /// at the end of the import process to ensure those settings can be made too. - /// - /// HOWEVER: The above isn't a problem Umbraco 10+ - the references can be set - /// before the actual doctypes exist, so we can do that in one pass. - /// - /// HOWEVER: If we move deletes to the end , we still need to process them. - /// but deletes are always 'change' = 'Hidden', so we only process hidden changes - /// - public override async Task> ProcessPostImportAsync(IEnumerable actions, HandlerSettings config) - { - if (actions == null || !actions.Any()) return []; - - var results = new List(); - var options = new uSyncImportOptions { Flags = SerializerFlags.LastPass }; - - // we only do deletes here. - foreach (var action in actions.Where(x => x.Change == ChangeType.Hidden)) - { - if (action.FileName is null) continue; - results.AddRange( - await ImportAsync(action.FileName, config, options)); - } - - results.AddRange(await CleanFoldersAsync(Guid.Empty)); - - return results; - } - + /// /// Fetch a DataType Container from the DataTypeService /// diff --git a/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs b/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs index 9bae83a8b..11cc61840 100644 --- a/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs +++ b/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs @@ -10,6 +10,7 @@ using uSync.Core; using uSync.Core.Dependency; using uSync.Core.Models; +using uSync.Core.Tracking; namespace uSync.BackOffice.SyncHandlers.Interfaces; @@ -23,6 +24,9 @@ namespace uSync.BackOffice.SyncHandlers.Interfaces; /// public interface ISyncHandler { + string SerializeType { get; } + ISyncTrackerBase? BaseTracker { get; } + /// /// alias for handler, used when finding a handler /// diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs index 26a773b2c..c1933e48f 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; +using System.Xml.XPath; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; @@ -115,7 +116,19 @@ public virtual async Task> ProcessPostImportAsync(IEnum foreach (var action in actions.Where(x => x.Change == ChangeType.Hidden)) { if (action.FileName is null) continue; - results.AddRange(await ImportAsync(action.FileName, config, options)); + + if (syncFileService.FileExists(action.FileName)) + { + var xml = await syncFileService.LoadXElementAsync(action.FileName); + var node = xml.XPathSelectElement($"//Empty[@Key='{action.Key}']"); + if (node is null) continue; + results.AddRange( + await ImportElementAsync(node, action.FileName, config, options)); + } + else + { + await ImportAsync(action.FileName, config, options); + } } results.AddRange(await CleanFoldersAsync(Guid.Empty)); diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs index 05aaafc53..2d730188c 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs @@ -170,7 +170,17 @@ public abstract class SyncHandlerRoot protected readonly IShortStringHelper shortStringHelper; /// - /// Constructor, base for all handlers + /// The serializer's item type (this is what the xml-node name will be. + /// + public string SerializeType => serializer.ItemType; + + /// + /// tracker used by the seralizer. + /// + public ISyncTrackerBase? BaseTracker => trackers.FirstOrDefault() as ISyncTrackerBase; + + /// + /// Constructor, base for all handlers` /// public SyncHandlerRoot( ILogger> logger, @@ -272,7 +282,6 @@ public async Task> ImportAllAsync(string[] folders, Han { count++; - options.Callbacks?.Update?.Invoke($"Importing {Path.GetFileNameWithoutExtension(item.Path)}", count, total); var result = await ImportElementAsync(item.Node, item.FileName, config, options); foreach (var attempt in result) @@ -442,6 +451,21 @@ virtual public async Task> ImportAsync(string file, Han /// All Imports lead here /// virtual public async Task> ImportElementAsync(XElement node, string filename, HandlerSettings settings, uSyncImportOptions options) + { + if (node.Name.LocalName == this.serializer.ItemType + "s") + { + var actions = new List(); + foreach (var item in node.Elements()) + { + actions.AddRange(await ImportSingleElementAsync(new XElement(item), filename, settings, options)); + } + return actions; + } + + return await ImportSingleElementAsync(node, filename, settings, options); + } + + virtual public async Task> ImportSingleElementAsync(XElement node, string filename, HandlerSettings settings, uSyncImportOptions options) { if (!await ShouldImportAsync(node, settings)) { @@ -1191,9 +1215,28 @@ protected virtual async Task> ReportFolderAsync(string } /// - /// Report on any changes for a single XML node. + /// Report on any changes for a single XML node. (may contain multiple items in a single node). /// public virtual async Task> ReportElementAsync(XElement node, string filename, HandlerSettings settings, uSyncImportOptions options) + { + if (node.Name.LocalName == this.serializer.ItemType + "s") + { + var actions = new List(); + foreach (var item in node.Elements()) + { + var cleanItem = new XElement(item); + actions.AddRange(await ReportElementSingleAsync(cleanItem, filename, settings, options)); + } + return actions; + } + + return await ReportElementSingleAsync(node, filename, settings, options); + } + + /// + /// Report on any changes for a single XML node. + /// + public virtual async Task> ReportElementSingleAsync(XElement node, string filename, HandlerSettings settings, uSyncImportOptions options) { try { diff --git a/uSync.BackOffice/uSyncBackOfficeBuilderExtensions.cs b/uSync.BackOffice/uSyncBackOfficeBuilderExtensions.cs index c5c819bbc..eefa99efe 100644 --- a/uSync.BackOffice/uSyncBackOfficeBuilderExtensions.cs +++ b/uSync.BackOffice/uSyncBackOfficeBuilderExtensions.cs @@ -103,6 +103,9 @@ public static IUmbracoBuilder AdduSync(this IUmbracoBuilder builder, Action(StatusCodes.Status200OK)] + public async Task MergeExportFolder() + { + var folders = _configService.GetFolders(); + var handlers = _handlerFactory.GetValidHandlers(new BackOffice.SyncHandlers.Models.SyncHandlerOptions { Set = _configService.Settings.DefaultSet }); + var result = await _syncService.MergeExportFolder(folders, handlers, false); + return Ok(result); + } +} diff --git a/uSync.Backoffice.Management.Api/Controllers/Actions/uSyncActionsController.cs b/uSync.Backoffice.Management.Api/Controllers/Actions/uSyncActionsController.cs index 0de5a0a02..b4bfb55ae 100644 --- a/uSync.Backoffice.Management.Api/Controllers/Actions/uSyncActionsController.cs +++ b/uSync.Backoffice.Management.Api/Controllers/Actions/uSyncActionsController.cs @@ -33,7 +33,7 @@ public async Task> GetActions() public async Task> GetActionsBySet(string setName) { return await Task.FromResult(_syncManagementService.GetActions(setName)); - } + } } diff --git a/uSync.Backoffice.Management.Client/usync-assets/package-lock.json b/uSync.Backoffice.Management.Client/usync-assets/package-lock.json index 68b639adc..d28fd86c3 100644 --- a/uSync.Backoffice.Management.Client/usync-assets/package-lock.json +++ b/uSync.Backoffice.Management.Client/usync-assets/package-lock.json @@ -1,12 +1,12 @@ { "name": "@jumoo/usync", - "version": "16.1.0", + "version": "16.1.0-build.20251013.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@jumoo/usync", - "version": "16.1.0", + "version": "16.1.0-build.20251013.4", "license": "MPL-2.0", "devDependencies": { "@hey-api/client-fetch": "^0.10.0", diff --git a/uSync.Backoffice.Management.Client/usync-assets/package.json b/uSync.Backoffice.Management.Client/usync-assets/package.json index 3792e7214..9c0e35f87 100644 --- a/uSync.Backoffice.Management.Client/usync-assets/package.json +++ b/uSync.Backoffice.Management.Client/usync-assets/package.json @@ -8,7 +8,7 @@ "homepage": "https://jumoo.co.uk/uSync", "license": "MPL-2.0", "type": "module", - "version": "16.1.0", + "version": "16.1.0-build.20251013.4", "main": "./dist/usync.js", "types": "./dist/index.d.ts", "module": "./dist/usync.js", From 7f11789118ace1edd2ccb21e3e1d054528d8c440 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Tue, 14 Oct 2025 11:11:23 +0100 Subject: [PATCH 02/28] ajust the interface, so we can have nice defaults for new properties. --- uSync.BackOffice/Services/SyncService_Files.cs | 13 +++++++++++-- .../SyncHandlers/Interfaces/ISyncHandler.cs | 11 +++++++++-- uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs | 6 +++--- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/uSync.BackOffice/Services/SyncService_Files.cs b/uSync.BackOffice/Services/SyncService_Files.cs index cbe24b979..f9f596ec2 100644 --- a/uSync.BackOffice/Services/SyncService_Files.cs +++ b/uSync.BackOffice/Services/SyncService_Files.cs @@ -1,4 +1,6 @@ -using System; +using Microsoft.Extensions.Logging; + +using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; @@ -120,7 +122,14 @@ public async Task MergeExportFolder(string[] paths, IEnumerable Path.Combine(x, handler.Handler.DefaultFolder)).ToArray(); var target = _uSyncConfig.Settings.ProductionFolder; - totalMerged += await _syncFileService.MakeSingleExportFromFolders(folders, handler.Handler.SerializeType, handler.Handler.BaseTracker, target, handler.Handler.DefaultFolder); + var serializerType = handler.Handler.GetSerializeType(); + if (serializerType is null) + { + _logger.LogWarning("Handler {Handler} does not support file export", handler.Handler.Alias); + continue; + } + + totalMerged += await _syncFileService.MakeSingleExportFromFolders(folders, serializerType, handler.Handler.GetBaseTracker(), target, handler.Handler.DefaultFolder); } return totalMerged; diff --git a/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs b/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs index 11cc61840..32bb654ec 100644 --- a/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs +++ b/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs @@ -24,8 +24,15 @@ namespace uSync.BackOffice.SyncHandlers.Interfaces; /// public interface ISyncHandler { - string SerializeType { get; } - ISyncTrackerBase? BaseTracker { get; } + /// + /// get the serializer type for the handler (e.g the name used in the xml) + /// + string? GetSerializeType() => null; + + /// + /// gets the base tracker from the serializer (used to track changes, merge items). + /// + ISyncTrackerBase? GetBaseTracker() => null; /// /// alias for handler, used when finding a handler diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs index 2d730188c..43a31f3fe 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs @@ -172,12 +172,12 @@ public abstract class SyncHandlerRoot /// /// The serializer's item type (this is what the xml-node name will be. /// - public string SerializeType => serializer.ItemType; + public string? GetSerializerType() => serializer.ItemType; /// - /// tracker used by the seralizer. + /// tracker used by the serializer. /// - public ISyncTrackerBase? BaseTracker => trackers.FirstOrDefault() as ISyncTrackerBase; + public ISyncTrackerBase? GetBaseTracker() => trackers.FirstOrDefault() as ISyncTrackerBase; /// /// Constructor, base for all handlers` From f63a311d86ac95cccc503a57ad62cac44e829ff0 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Tue, 14 Oct 2025 11:59:43 +0100 Subject: [PATCH 03/28] clean up the merge function --- uSync.BackOffice/Services/ISyncFileService.cs | 2 +- uSync.BackOffice/Services/SyncFileService.cs | 11 +++++------ uSync.BackOffice/Services/SyncService_Files.cs | 14 ++++++++------ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/uSync.BackOffice/Services/ISyncFileService.cs b/uSync.BackOffice/Services/ISyncFileService.cs index ca4858437..2aeaea521 100644 --- a/uSync.BackOffice/Services/ISyncFileService.cs +++ b/uSync.BackOffice/Services/ISyncFileService.cs @@ -138,7 +138,7 @@ public interface ISyncFileService /// /// merge all the files in the give folders into a single xml node, that can be bulk imported /// - Task MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string targetFolder, string fileName); + Task MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string fileName, string extension); /// /// merge a list of files into a single XElement diff --git a/uSync.BackOffice/Services/SyncFileService.cs b/uSync.BackOffice/Services/SyncFileService.cs index 4a4925319..266e09fe9 100644 --- a/uSync.BackOffice/Services/SyncFileService.cs +++ b/uSync.BackOffice/Services/SyncFileService.cs @@ -324,9 +324,9 @@ public void CopyFolder(string source, string target) } } - public async Task MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string targetFolder, string filename) + public async Task MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string filename, string extension) { - var merged = await MergeFoldersAsync(folders, "config", trackerBase); + var merged = await MergeFoldersAsync(folders, extension, trackerBase); var megaNode = new XElement(itemType + "s"); int count = 0; @@ -336,11 +336,10 @@ public async Task MakeSingleExportFromFolders(string[] folders, string item megaNode.Add(new XElement(item.Node)); } - var resolvedTargetFolder = GetAbsPath(targetFolder); - CreateFolder(resolvedTargetFolder); + var resolvedTargetFile = GetAbsPath(filename); + CreateFoldersForFile(resolvedTargetFile); - var singleFileName = Path.Combine(resolvedTargetFolder, $"{filename}.config"); - await SaveXElementAsync(megaNode, singleFileName); + await SaveXElementAsync(megaNode, filename); return count; } diff --git a/uSync.BackOffice/Services/SyncService_Files.cs b/uSync.BackOffice/Services/SyncService_Files.cs index f9f596ec2..294890601 100644 --- a/uSync.BackOffice/Services/SyncService_Files.cs +++ b/uSync.BackOffice/Services/SyncService_Files.cs @@ -119,17 +119,19 @@ public async Task MergeExportFolder(string[] paths, IEnumerable Path.Combine(x, handler.Handler.DefaultFolder)).ToArray(); - var target = _uSyncConfig.Settings.ProductionFolder; - var serializerType = handler.Handler.GetSerializeType(); - if (serializerType is null) + var baseTracker = handler.Handler.GetBaseTracker(); + if (serializerType is null || baseTracker is null) { - _logger.LogWarning("Handler {Handler} does not support file export", handler.Handler.Alias); + _logger.LogWarning("Handler {Handler} does not support file merging", handler.Handler.Alias); continue; } - totalMerged += await _syncFileService.MakeSingleExportFromFolders(folders, serializerType, handler.Handler.GetBaseTracker(), target, handler.Handler.DefaultFolder); + var folders = paths.Select(x => Path.Combine(x, handler.Handler.DefaultFolder)).ToArray(); + var targetFileName = Path.Combine(_uSyncConfig.Settings.ProductionFolder, + handler.Handler.DefaultFolder + "." + _uSyncConfig.Settings.DefaultExtension); + + totalMerged += await _syncFileService.MakeSingleExportFromFolders(folders, serializerType, baseTracker, targetFileName, _uSyncConfig.Settings.DefaultExtension); } return totalMerged; From 17d865be07c302a68a4e12f4bdc21d6f4b025ebc Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Tue, 14 Oct 2025 12:00:10 +0100 Subject: [PATCH 04/28] make sure individual empty files process the 'current' way (we don't want to slow down existing imports). --- .../SyncHandlers/SyncHandlerContainerBase.cs | 25 +++++++++---------- .../Serialization/SyncSerializerRoot.cs | 2 +- uSync.Core/uSyncConstants.cs | 3 +++ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs index c1933e48f..6a8d672b5 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs @@ -116,19 +116,18 @@ public virtual async Task> ProcessPostImportAsync(IEnum foreach (var action in actions.Where(x => x.Change == ChangeType.Hidden)) { if (action.FileName is null) continue; - - if (syncFileService.FileExists(action.FileName)) - { - var xml = await syncFileService.LoadXElementAsync(action.FileName); - var node = xml.XPathSelectElement($"//Empty[@Key='{action.Key}']"); - if (node is null) continue; - results.AddRange( - await ImportElementAsync(node, action.FileName, config, options)); - } - else - { - await ImportAsync(action.FileName, config, options); - } + if (syncFileService.FileExists(action.FileName) is false) continue; + + // single + var xml = await syncFileService.LoadXElementAsync(action.FileName); + if (xml.Name.LocalName.Equals(Core.uSyncConstants.Serialization.Empty) is true) + return await ImportElementAsync(xml, action.FileName, config, options); + + // multiple ? + var node = xml.XPathSelectElement($"//{Core.uSyncConstants.Serialization.Empty}[@Key='{action.Key}']"); + if (node is null) continue; + + results.AddRange(await ImportElementAsync(node, action.FileName, config, options)); } results.AddRange(await CleanFoldersAsync(Guid.Empty)); diff --git a/uSync.Core/Serialization/SyncSerializerRoot.cs b/uSync.Core/Serialization/SyncSerializerRoot.cs index cfac15cd2..017225de8 100644 --- a/uSync.Core/Serialization/SyncSerializerRoot.cs +++ b/uSync.Core/Serialization/SyncSerializerRoot.cs @@ -281,7 +281,7 @@ public virtual Task> SerializeEmptyAsync(TObject item, Syn var node = XElementExtensions.MakeEmpty(ItemKey(item), change, alias); - return Task.FromResult(SyncAttempt.Succeed("Empty", node, ChangeType.Removed, [])); + return Task.FromResult(SyncAttempt.Succeed(uSyncConstants.Serialization.Empty, node, ChangeType.Removed, [])); } diff --git a/uSync.Core/uSyncConstants.cs b/uSync.Core/uSyncConstants.cs index bf84c7661..3057ba3d6 100644 --- a/uSync.Core/uSyncConstants.cs +++ b/uSync.Core/uSyncConstants.cs @@ -49,6 +49,9 @@ public static class Serialization public const string Domain = "Domain"; + /// + /// action files are 'empty' with an action to say what they do. + /// public const string Empty = "Empty"; public const string RelationType = "RelationType"; From 74f6470ac981e9d79d465bfe08413cf3f8174017 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Tue, 14 Oct 2025 12:53:26 +0100 Subject: [PATCH 05/28] cache the loading of xml files in the clean loop, to stop multiple file loads. --- .../SyncHandlers/SyncHandlerContainerBase.cs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs index 6a8d672b5..5934766a3 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs @@ -112,24 +112,35 @@ public virtual async Task> ProcessPostImportAsync(IEnum var results = new List(); var options = new uSyncImportOptions { Flags = SerializerFlags.LastPass }; + // a cache of loaded files so we don't keep loading the same file multiple times + // in production mode the same file might contain multiple 'empty' nodes + // and they can be large, and require loading from disk multiple times would slow + // it all down. + Dictionary _loadedFiles = []; + // we only do deletes here. foreach (var action in actions.Where(x => x.Change == ChangeType.Hidden)) { if (action.FileName is null) continue; if (syncFileService.FileExists(action.FileName) is false) continue; + // load the file if we haven't already + if (_loadedFiles.TryGetValue(action.FileName, out XElement? xml) is false) + _loadedFiles[action.FileName] = await syncFileService.LoadXElementAsync(action.FileName); + // single - var xml = await syncFileService.LoadXElementAsync(action.FileName); - if (xml.Name.LocalName.Equals(Core.uSyncConstants.Serialization.Empty) is true) - return await ImportElementAsync(xml, action.FileName, config, options); + if (_loadedFiles[action.FileName].Name.LocalName.Equals(Core.uSyncConstants.Serialization.Empty) is true) + return await ImportElementAsync(_loadedFiles[action.FileName], action.FileName, config, options); // multiple ? - var node = xml.XPathSelectElement($"//{Core.uSyncConstants.Serialization.Empty}[@Key='{action.Key}']"); + var node = _loadedFiles[action.FileName].XPathSelectElement($"//{Core.uSyncConstants.Serialization.Empty}[@Key='{action.Key}']"); if (node is null) continue; - + results.AddRange(await ImportElementAsync(node, action.FileName, config, options)); } + _loadedFiles.Clear(); + results.AddRange(await CleanFoldersAsync(Guid.Empty)); return results; From 31eb694658c691ae0a307e6763eff6c7503a3c51 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Tue, 14 Oct 2025 12:53:38 +0100 Subject: [PATCH 06/28] clean up the import/report methods to make them more readable. --- .../Services/SyncService_Handlers.cs | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/uSync.BackOffice/Services/SyncService_Handlers.cs b/uSync.BackOffice/Services/SyncService_Handlers.cs index 350e11e46..810be739e 100644 --- a/uSync.BackOffice/Services/SyncService_Handlers.cs +++ b/uSync.BackOffice/Services/SyncService_Handlers.cs @@ -38,16 +38,17 @@ public async Task> ReportHandlerAsync(string handler, u if (handlerPair == null) return []; var folders = GetHandlerFolders(GetFolderFromOptions(options), handlerPair.Handler); - if (File.Exists(folders.Last() + ".config")) - { - var fileName = folders.Last() + ".config"; - var node = await _syncFileService.LoadXElementAsync(fileName); - return await handlerPair.Handler.ReportElementAsync(node, fileName, handlerPair.Settings, options); - } - else - { - return await handlerPair.Handler.ReportAsync(folders, handlerPair.Settings, options.Callbacks?.Update); - } + var productionFile = $"{folders.Last()}.{_uSyncConfig.Settings.DefaultExtension}"; + if (File.Exists(productionFile)) + return await ReportMergedFile(productionFile, handlerPair, options); + + return await handlerPair.Handler.ReportAsync(folders, handlerPair.Settings, options.Callbacks?.Update); + } + + private async Task> ReportMergedFile(string filename, HandlerConfigPair handlerPair, uSyncImportOptions options) + { + var node = await _syncFileService.LoadXElementAsync(filename); + return await handlerPair.Handler.ReportElementAsync(node, filename, handlerPair.Settings, options); } /// > @@ -79,21 +80,13 @@ public async Task> ImportHandlerAsync(string handlerAli List results; - if (File.Exists(folders.Last() + ".config")) - { - var fileName = folders.Last() + ".config"; - var node = await _syncFileService.LoadXElementAsync(fileName); - results = [.. await handlerPair.Handler.ImportElementAsync(node, fileName, handlerPair.Settings, options)]; - } + var productionFile = $"{folders.Last()}.{_uSyncConfig.Settings.DefaultExtension}"; + if (File.Exists(productionFile)) + results = [.. await ImportMergedFile(productionFile, handlerPair, options)]; else - { - results = [..await handlerPair.Handler.ImportAllAsync(folders, handlerPair.Settings, options)]; - } - - // _logger.LogDebug("< Import Handler {handler}", handlerAlias); + results = [.. await handlerPair.Handler.ImportAllAsync(folders, handlerPair.Settings, options)]; scope?.Complete(); - return results; } } @@ -103,6 +96,12 @@ public async Task> ImportHandlerAsync(string handlerAli } } + private async Task> ImportMergedFile(string filename, HandlerConfigPair handlerPair, uSyncImportOptions options) + { + var node = await _syncFileService.LoadXElementAsync(filename); + return await handlerPair.Handler.ImportElementAsync(node, filename, handlerPair.Settings, options); + } + /// > public async Task> PerformPostImportAsync(string[] folders, string handlerSet, IEnumerable actions) { From 5270df1b90d84f00c6144dc4ea38393ebe467df8 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Tue, 14 Oct 2025 14:55:09 +0100 Subject: [PATCH 07/28] add incremental update messages so we can smooth out progress even when using a single import file. --- uSync.BackOffice/Hubs/HubClientService.cs | 18 ++++++++++- uSync.BackOffice/Hubs/uSyncCallbacks.cs | 30 ++++++++++++++++++- uSync.BackOffice/Services/SyncService.cs | 6 ---- .../SyncHandlers/Interfaces/ISyncHandler.cs | 5 ---- .../SyncHandlers/SyncHandlerRoot.cs | 8 ++++- .../usync-assets/package-lock.json | 4 +-- .../usync-assets/package.json | 2 +- 7 files changed, 56 insertions(+), 17 deletions(-) diff --git a/uSync.BackOffice/Hubs/HubClientService.cs b/uSync.BackOffice/Hubs/HubClientService.cs index 4ec28edca..1dc700590 100644 --- a/uSync.BackOffice/Hubs/HubClientService.cs +++ b/uSync.BackOffice/Hubs/HubClientService.cs @@ -80,5 +80,21 @@ public void PostUpdate(string message, int count, int total) /// get the uSync callbacks for this connection /// /// - public uSyncCallbacks Callbacks() => new(this.PostSummary, this.PostUpdate); + public uSyncCallbacks Callbacks() => new(this.PostSummary, this.PostUpdate, this.SetCountRange, this.PostIncrementalUpdate); + + private int _start = 0; + private int _end = 0; + + public void SetCountRange(int start, int end) + { + _start = start; + _end = end; + } + + public void PostIncrementalUpdate(string message) + { + _start++; + if (_start > _end) _end = _start; + this.PostUpdate(message, _start, _end); + } } diff --git a/uSync.BackOffice/Hubs/uSyncCallbacks.cs b/uSync.BackOffice/Hubs/uSyncCallbacks.cs index baddc009e..dd6e67d74 100644 --- a/uSync.BackOffice/Hubs/uSyncCallbacks.cs +++ b/uSync.BackOffice/Hubs/uSyncCallbacks.cs @@ -1,7 +1,24 @@ -using uSync.BackOffice.SyncHandlers.Interfaces; +using Org.BouncyCastle.Bcpg.OpenPgp; + +using uSync.BackOffice.Models; +using uSync.BackOffice.SyncHandlers.Interfaces; namespace uSync.BackOffice; +/// +/// Callback event for SignalR hub +/// +public delegate void SyncEventCallback(SyncProgressSummary summary); + +/// +/// callback delegate for SignalR messaging +/// +public delegate void SyncUpdateCallback(string message, int count, int total); + +public delegate void SyncSetUpdateRange(int start, int end); +public delegate void SyncIncrementalUpdateCallback(string message); + + /// /// Callback objects used to communicate via SignalR /// @@ -17,6 +34,10 @@ public class uSyncCallbacks /// public SyncUpdateCallback? Update { get; private set; } + public SyncSetUpdateRange? SetRange { get; private set; } + + public SyncIncrementalUpdateCallback? IncrementalUpdate { get; private set; } + /// /// generate a new callback object /// @@ -25,4 +46,11 @@ public uSyncCallbacks(SyncEventCallback? callback, SyncUpdateCallback? update) this.Callback = callback; this.Update = update; } + + public uSyncCallbacks(SyncEventCallback? callback, SyncUpdateCallback? update, SyncSetUpdateRange? updateRange, SyncIncrementalUpdateCallback incrementalUpdate) + : this(callback, update) + { + this.SetRange = updateRange; + this.IncrementalUpdate = incrementalUpdate; + } } diff --git a/uSync.BackOffice/Services/SyncService.cs b/uSync.BackOffice/Services/SyncService.cs index 0efa3a9d6..6332db6be 100644 --- a/uSync.BackOffice/Services/SyncService.cs +++ b/uSync.BackOffice/Services/SyncService.cs @@ -27,12 +27,6 @@ namespace uSync.BackOffice; - -/// -/// Callback event for SignalR hub -/// -public delegate void SyncEventCallback(SyncProgressSummary summary); - /// /// the service that does all the processing, /// this forms the entry point as an API to diff --git a/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs b/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs index 32bb654ec..bf0c06d72 100644 --- a/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs +++ b/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs @@ -14,11 +14,6 @@ namespace uSync.BackOffice.SyncHandlers.Interfaces; -/// -/// callback delegate for SignalR messaging -/// -public delegate void SyncUpdateCallback(string message, int count, int total); - /// /// Handler interface for anything that wants to process elements via uSync /// diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs index 43a31f3fe..f05876e01 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs @@ -278,6 +278,8 @@ public async Task> ImportAllAsync(string[] folders, Han int count = 0; int total = items.Count; + options.Callbacks?.SetRange?.Invoke(count, total); + foreach (var item in items) { count++; @@ -455,7 +457,9 @@ virtual public async Task> ImportElementAsync(XElement if (node.Name.LocalName == this.serializer.ItemType + "s") { var actions = new List(); - foreach (var item in node.Elements()) + var elements = node.Elements().ToList(); + options.Callbacks?.SetRange?.Invoke(0, elements.Count); + foreach (var item in elements) { actions.AddRange(await ImportSingleElementAsync(new XElement(item), filename, settings, options)); } @@ -483,6 +487,8 @@ virtual public async Task> ImportSingleElementAsync(XEl try { + options.Callbacks?.IncrementalUpdate?.Invoke(node.GetAlias()); + // merge the options from the handler and any import options into our serializer options. var serializerOptions = new SyncSerializerOptions(options.Flags, settings.Settings, options.UserId); serializerOptions.MergeSettings(options.Settings); diff --git a/uSync.Backoffice.Management.Client/usync-assets/package-lock.json b/uSync.Backoffice.Management.Client/usync-assets/package-lock.json index d28fd86c3..68b639adc 100644 --- a/uSync.Backoffice.Management.Client/usync-assets/package-lock.json +++ b/uSync.Backoffice.Management.Client/usync-assets/package-lock.json @@ -1,12 +1,12 @@ { "name": "@jumoo/usync", - "version": "16.1.0-build.20251013.4", + "version": "16.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@jumoo/usync", - "version": "16.1.0-build.20251013.4", + "version": "16.1.0", "license": "MPL-2.0", "devDependencies": { "@hey-api/client-fetch": "^0.10.0", diff --git a/uSync.Backoffice.Management.Client/usync-assets/package.json b/uSync.Backoffice.Management.Client/usync-assets/package.json index 9c0e35f87..3792e7214 100644 --- a/uSync.Backoffice.Management.Client/usync-assets/package.json +++ b/uSync.Backoffice.Management.Client/usync-assets/package.json @@ -8,7 +8,7 @@ "homepage": "https://jumoo.co.uk/uSync", "license": "MPL-2.0", "type": "module", - "version": "16.1.0-build.20251013.4", + "version": "16.1.0", "main": "./dist/usync.js", "types": "./dist/index.d.ts", "module": "./dist/usync.js", From a57c9909686d571601925064f8a4aab043638b24 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Tue, 14 Oct 2025 15:00:35 +0100 Subject: [PATCH 08/28] fix typo on exportOnSave settings localization string. --- .../src/workspace/views/settings/settings.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/workspace/views/settings/settings.element.ts b/uSync.Backoffice.Management.Client/usync-assets/src/workspace/views/settings/settings.element.ts index 80767a0bd..b3be1d3c7 100644 --- a/uSync.Backoffice.Management.Client/usync-assets/src/workspace/views/settings/settings.element.ts +++ b/uSync.Backoffice.Management.Client/usync-assets/src/workspace/views/settings/settings.element.ts @@ -60,7 +60,7 @@ export class USyncSettingsViewElement extends UmbElementMixin(LitElement) { .value=${this.settings?.exportAtStartup}> From 870fb83e7e0a67cc80e7ac09835bdf1d32ebf6bf Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Tue, 14 Oct 2025 15:12:31 +0100 Subject: [PATCH 09/28] XML Comments. --- .../appsettings-schema.usync.json | 2 +- uSync.BackOffice/Configuration/uSyncSettings.cs | 14 ++++++++++++++ uSync.BackOffice/Hubs/HubClientService.cs | 6 ++++++ uSync.BackOffice/Hubs/uSyncCallbacks.cs | 17 +++++++++++++++++ .../SyncHandlers/SyncHandlerContainerBase.cs | 4 ++-- .../SyncHandlers/SyncHandlerRoot.cs | 9 ++++++++- 6 files changed, 48 insertions(+), 4 deletions(-) diff --git a/uSync.BackOffice.Targets/appsettings-schema.usync.json b/uSync.BackOffice.Targets/appsettings-schema.usync.json index 576d48c7d..505960b18 100644 --- a/uSync.BackOffice.Targets/appsettings-schema.usync.json +++ b/uSync.BackOffice.Targets/appsettings-schema.usync.json @@ -236,7 +236,7 @@ }, "USyncBackOfficeConfigurationSyncFolderMode": { "type": "string", - "description": "", + "description": "uSync's folder mode - normal, root or production\n ", "x-enumNames": [ "Normal", "Root", diff --git a/uSync.BackOffice/Configuration/uSyncSettings.cs b/uSync.BackOffice/Configuration/uSyncSettings.cs index 6d1a2ca17..92398ef24 100644 --- a/uSync.BackOffice/Configuration/uSyncSettings.cs +++ b/uSync.BackOffice/Configuration/uSyncSettings.cs @@ -249,10 +249,24 @@ public class uSyncSettings public string ProductionFolder { get; set; } = "uSync/production"; } +/// +/// uSync's folder mode - normal, root or production +/// public enum SyncFolderMode { + /// + /// normal - expects individual files in the uSync folder(s) + /// Normal, + + /// + /// root - will read and write things to the root folder, + /// Root, + + /// + /// production - looks in a 'production' folder, expects a single file per handler. + /// Production, }; diff --git a/uSync.BackOffice/Hubs/HubClientService.cs b/uSync.BackOffice/Hubs/HubClientService.cs index 1dc700590..6dc43e532 100644 --- a/uSync.BackOffice/Hubs/HubClientService.cs +++ b/uSync.BackOffice/Hubs/HubClientService.cs @@ -85,12 +85,18 @@ public void PostUpdate(string message, int count, int total) private int _start = 0; private int _end = 0; + /// + /// set a range (start to end) that we except the next set of updates to be bound within. + /// public void SetCountRange(int start, int end) { _start = start; _end = end; } + /// + /// post an update and increment the counter by one. + /// public void PostIncrementalUpdate(string message) { _start++; diff --git a/uSync.BackOffice/Hubs/uSyncCallbacks.cs b/uSync.BackOffice/Hubs/uSyncCallbacks.cs index dd6e67d74..932863b99 100644 --- a/uSync.BackOffice/Hubs/uSyncCallbacks.cs +++ b/uSync.BackOffice/Hubs/uSyncCallbacks.cs @@ -15,7 +15,15 @@ namespace uSync.BackOffice; /// public delegate void SyncUpdateCallback(string message, int count, int total); +/// +/// callback delegate to set the start and end rage for the update counters. +/// public delegate void SyncSetUpdateRange(int start, int end); + +/// +/// callback to send a update message and increment the counter by one so moving the progress bar. +/// +/// public delegate void SyncIncrementalUpdateCallback(string message); @@ -34,8 +42,14 @@ public class uSyncCallbacks /// public SyncUpdateCallback? Update { get; private set; } + /// + /// set a start and end range for the counter. + /// public SyncSetUpdateRange? SetRange { get; private set; } + /// + /// update and increment callback. + /// public SyncIncrementalUpdateCallback? IncrementalUpdate { get; private set; } /// @@ -47,6 +61,9 @@ public uSyncCallbacks(SyncEventCallback? callback, SyncUpdateCallback? update) this.Update = update; } + /// + /// generate a callback object with range and incremental update + /// public uSyncCallbacks(SyncEventCallback? callback, SyncUpdateCallback? update, SyncSetUpdateRange? updateRange, SyncIncrementalUpdateCallback incrementalUpdate) : this(callback, update) { diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs index 5934766a3..b5b84021e 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs @@ -130,13 +130,13 @@ public virtual async Task> ProcessPostImportAsync(IEnum // single if (_loadedFiles[action.FileName].Name.LocalName.Equals(Core.uSyncConstants.Serialization.Empty) is true) - return await ImportElementAsync(_loadedFiles[action.FileName], action.FileName, config, options); + return await ImportSingleElementAsync(_loadedFiles[action.FileName], action.FileName, config, options); // multiple ? var node = _loadedFiles[action.FileName].XPathSelectElement($"//{Core.uSyncConstants.Serialization.Empty}[@Key='{action.Key}']"); if (node is null) continue; - results.AddRange(await ImportElementAsync(node, action.FileName, config, options)); + results.AddRange(await ImportSingleElementAsync(node, action.FileName, config, options)); } _loadedFiles.Clear(); diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs index f05876e01..2b553cbf8 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs @@ -469,7 +469,14 @@ virtual public async Task> ImportElementAsync(XElement return await ImportSingleElementAsync(node, filename, settings, options); } - virtual public async Task> ImportSingleElementAsync(XElement node, string filename, HandlerSettings settings, uSyncImportOptions options) + /// + /// import a single XElement into umbraco. + /// + /// + /// if the XElement contains multiple entries, then this method will not import them, if there is a possibility of that + /// then the ImportElementAsync method should be used - which splits them before loading this call. + /// + virtual protected async Task> ImportSingleElementAsync(XElement node, string filename, HandlerSettings settings, uSyncImportOptions options) { if (!await ShouldImportAsync(node, settings)) { From 09ed27556d39a06fe708496de8566ac811bcffb4 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Tue, 14 Oct 2025 15:16:12 +0100 Subject: [PATCH 10/28] more xml comments --- uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs index 2b553cbf8..323f5f934 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs @@ -446,12 +446,7 @@ virtual public async Task> ImportAsync(string file, Han return await ImportAsync(file, config, options); } - /// - /// Import a node, with settings and options - /// - /// - /// All Imports lead here - /// + /// virtual public async Task> ImportElementAsync(XElement node, string filename, HandlerSettings settings, uSyncImportOptions options) { if (node.Name.LocalName == this.serializer.ItemType + "s") From 65167b9a915b046c290ab2aa5baf076d65a5b1b4 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 11:51:57 +0100 Subject: [PATCH 11/28] Update uSync.BackOffice/Hubs/uSyncCallbacks.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- uSync.BackOffice/Hubs/uSyncCallbacks.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uSync.BackOffice/Hubs/uSyncCallbacks.cs b/uSync.BackOffice/Hubs/uSyncCallbacks.cs index 932863b99..6aa8c9203 100644 --- a/uSync.BackOffice/Hubs/uSyncCallbacks.cs +++ b/uSync.BackOffice/Hubs/uSyncCallbacks.cs @@ -16,7 +16,7 @@ namespace uSync.BackOffice; public delegate void SyncUpdateCallback(string message, int count, int total); /// -/// callback delegate to set the start and end rage for the update counters. +/// callback delegate to set the start and end range for the update counters. /// public delegate void SyncSetUpdateRange(int start, int end); From a51e2c5131abeb7cc4f77c21297b2c1b977f537d Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 11:52:06 +0100 Subject: [PATCH 12/28] Update uSync.BackOffice/Configuration/uSyncSettings.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- uSync.BackOffice/Configuration/uSyncSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uSync.BackOffice/Configuration/uSyncSettings.cs b/uSync.BackOffice/Configuration/uSyncSettings.cs index 92398ef24..7f42335d5 100644 --- a/uSync.BackOffice/Configuration/uSyncSettings.cs +++ b/uSync.BackOffice/Configuration/uSyncSettings.cs @@ -197,7 +197,7 @@ public class uSyncSettings public string HideAddOns { get; set; } = "licence"; /// - /// turns of use of the Notifications.Suppress method, so notifications + /// turns off use of the Notifications.Suppress method, so notifications /// fire after every item is imported. /// /// From f815e9894260f64351d1863149fbb25ee85885fd Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 11:52:17 +0100 Subject: [PATCH 13/28] Update uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs index 323f5f934..602923079 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs @@ -170,7 +170,7 @@ public abstract class SyncHandlerRoot protected readonly IShortStringHelper shortStringHelper; /// - /// The serializer's item type (this is what the xml-node name will be. + /// The serializer's item type (this is what the xml-node name will be). /// public string? GetSerializerType() => serializer.ItemType; From 3a6d585fae04ca03a86c733a7e802a474cd38a89 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 11:52:32 +0100 Subject: [PATCH 14/28] Update uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs index 602923079..cdfaac4b8 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs @@ -180,7 +180,7 @@ public abstract class SyncHandlerRoot public ISyncTrackerBase? GetBaseTracker() => trackers.FirstOrDefault() as ISyncTrackerBase; /// - /// Constructor, base for all handlers` + /// Constructor, base for all handlers /// public SyncHandlerRoot( ILogger> logger, From 060d73d982a1ba0823ba343099317239365d227e Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 11:52:48 +0100 Subject: [PATCH 15/28] Update uSync.BackOffice/Configuration/uSyncSettings.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- uSync.BackOffice/Configuration/uSyncSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uSync.BackOffice/Configuration/uSyncSettings.cs b/uSync.BackOffice/Configuration/uSyncSettings.cs index 7f42335d5..08eb0c7c5 100644 --- a/uSync.BackOffice/Configuration/uSyncSettings.cs +++ b/uSync.BackOffice/Configuration/uSyncSettings.cs @@ -238,7 +238,7 @@ public class uSyncSettings /// /// What type of mode the folder should work in (default, root, or production) /// - [DefaultValue("Normal")] + [DefaultValue(SyncFolderMode.Normal)] public SyncFolderMode FolderMode { get; set; } = SyncFolderMode.Normal; /// From 115e49a003795df536d0f91094b19ceb391cfc60 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 11:54:06 +0100 Subject: [PATCH 16/28] Update uSync.BackOffice/Services/SyncService_Files.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- uSync.BackOffice/Services/SyncService_Files.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/uSync.BackOffice/Services/SyncService_Files.cs b/uSync.BackOffice/Services/SyncService_Files.cs index 294890601..97bddcf17 100644 --- a/uSync.BackOffice/Services/SyncService_Files.cs +++ b/uSync.BackOffice/Services/SyncService_Files.cs @@ -115,7 +115,6 @@ private static string CleanPathForZip(string path) public async Task MergeExportFolder(string[] paths, IEnumerable handlers, bool clean) { var totalMerged = 0; - var root = _uSyncConfig.GetWorkingFolder(); foreach (var handler in handlers) { From 3a3763f16b7cf294df9f31b7329a7c53c263df9d Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 11:54:55 +0100 Subject: [PATCH 17/28] Update uSync.BackOffice/Services/SyncFileService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- uSync.BackOffice/Services/SyncFileService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uSync.BackOffice/Services/SyncFileService.cs b/uSync.BackOffice/Services/SyncFileService.cs index 266e09fe9..9d79aa015 100644 --- a/uSync.BackOffice/Services/SyncFileService.cs +++ b/uSync.BackOffice/Services/SyncFileService.cs @@ -339,7 +339,7 @@ public async Task MakeSingleExportFromFolders(string[] folders, string item var resolvedTargetFile = GetAbsPath(filename); CreateFoldersForFile(resolvedTargetFile); - await SaveXElementAsync(megaNode, filename); + await SaveXElementAsync(megaNode, resolvedTargetFile); return count; } From 1bb1d9fddf8d1cf27b46a6bc3b6aff58bc2d60cb Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 11:55:04 +0100 Subject: [PATCH 18/28] Update uSync.BackOffice/Hubs/uSyncCallbacks.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- uSync.BackOffice/Hubs/uSyncCallbacks.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/uSync.BackOffice/Hubs/uSyncCallbacks.cs b/uSync.BackOffice/Hubs/uSyncCallbacks.cs index 6aa8c9203..fc94fc782 100644 --- a/uSync.BackOffice/Hubs/uSyncCallbacks.cs +++ b/uSync.BackOffice/Hubs/uSyncCallbacks.cs @@ -1,5 +1,3 @@ -using Org.BouncyCastle.Bcpg.OpenPgp; - using uSync.BackOffice.Models; using uSync.BackOffice.SyncHandlers.Interfaces; From 3f7a6ba30849941be7725506d61660a3a9f9fd9b Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 11:55:32 +0100 Subject: [PATCH 19/28] make sure we process all single actions (Co-piolt fix) --- .../SyncHandlers/SyncHandlerContainerBase.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs index b5b84021e..d11425a84 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs @@ -128,15 +128,19 @@ public virtual async Task> ProcessPostImportAsync(IEnum if (_loadedFiles.TryGetValue(action.FileName, out XElement? xml) is false) _loadedFiles[action.FileName] = await syncFileService.LoadXElementAsync(action.FileName); - // single if (_loadedFiles[action.FileName].Name.LocalName.Equals(Core.uSyncConstants.Serialization.Empty) is true) - return await ImportSingleElementAsync(_loadedFiles[action.FileName], action.FileName, config, options); + { + // single + results.AddRange(await ImportSingleElementAsync(_loadedFiles[action.FileName], action.FileName, config, options)); + } + else + { + // multiple ? + var node = _loadedFiles[action.FileName].XPathSelectElement($"//{Core.uSyncConstants.Serialization.Empty}[@Key='{action.Key}']"); + if (node is null) continue; - // multiple ? - var node = _loadedFiles[action.FileName].XPathSelectElement($"//{Core.uSyncConstants.Serialization.Empty}[@Key='{action.Key}']"); - if (node is null) continue; - - results.AddRange(await ImportSingleElementAsync(node, action.FileName, config, options)); + results.AddRange(await ImportSingleElementAsync(node, action.FileName, config, options)); + } } _loadedFiles.Clear(); From c13d744bcf3b333032f6a09e3b9959987f6e6b4b Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 12:01:25 +0100 Subject: [PATCH 20/28] Update uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs b/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs index bf0c06d72..8450f5154 100644 --- a/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs +++ b/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs @@ -22,7 +22,7 @@ public interface ISyncHandler /// /// get the serializer type for the handler (e.g the name used in the xml) /// - string? GetSerializeType() => null; + string? GetSerializerType() => null; /// /// gets the base tracker from the serializer (used to track changes, merge items). From 5392700aa874bbe3189d96f2d30e34c67664544c Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 12:02:57 +0100 Subject: [PATCH 21/28] fix serializer interface typo. --- uSync.BackOffice/Services/SyncService_Files.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uSync.BackOffice/Services/SyncService_Files.cs b/uSync.BackOffice/Services/SyncService_Files.cs index 97bddcf17..ab41e713e 100644 --- a/uSync.BackOffice/Services/SyncService_Files.cs +++ b/uSync.BackOffice/Services/SyncService_Files.cs @@ -118,7 +118,7 @@ public async Task MergeExportFolder(string[] paths, IEnumerable Date: Wed, 15 Oct 2025 12:05:08 +0100 Subject: [PATCH 22/28] remove bool clean from merge public api --- uSync.BackOffice/Services/ISyncService.cs | 2 +- uSync.BackOffice/Services/SyncService_Files.cs | 2 +- .../Controllers/Actions/SyncFolderController.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uSync.BackOffice/Services/ISyncService.cs b/uSync.BackOffice/Services/ISyncService.cs index 3269faaf3..9ccce0492 100644 --- a/uSync.BackOffice/Services/ISyncService.cs +++ b/uSync.BackOffice/Services/ISyncService.cs @@ -160,6 +160,6 @@ public interface ISyncService /// /// merge the given folders in single 'production' files for each handler. /// - Task MergeExportFolder(string[] paths, IEnumerable handlers, bool clean); + Task MergeExportFolder(string[] paths, IEnumerable handlers); } \ No newline at end of file diff --git a/uSync.BackOffice/Services/SyncService_Files.cs b/uSync.BackOffice/Services/SyncService_Files.cs index ab41e713e..1d23577be 100644 --- a/uSync.BackOffice/Services/SyncService_Files.cs +++ b/uSync.BackOffice/Services/SyncService_Files.cs @@ -112,7 +112,7 @@ private static string CleanPathForZip(string path) .TrimEnd(Path.DirectorySeparatorChar); /// - public async Task MergeExportFolder(string[] paths, IEnumerable handlers, bool clean) + public async Task MergeExportFolder(string[] paths, IEnumerable handlers) { var totalMerged = 0; diff --git a/uSync.Backoffice.Management.Api/Controllers/Actions/SyncFolderController.cs b/uSync.Backoffice.Management.Api/Controllers/Actions/SyncFolderController.cs index 014ae01ef..2edfa02ce 100644 --- a/uSync.Backoffice.Management.Api/Controllers/Actions/SyncFolderController.cs +++ b/uSync.Backoffice.Management.Api/Controllers/Actions/SyncFolderController.cs @@ -31,7 +31,7 @@ public async Task MergeExportFolder() { var folders = _configService.GetFolders(); var handlers = _handlerFactory.GetValidHandlers(new BackOffice.SyncHandlers.Models.SyncHandlerOptions { Set = _configService.Settings.DefaultSet }); - var result = await _syncService.MergeExportFolder(folders, handlers, false); + var result = await _syncService.MergeExportFolder(folders, handlers); return Ok(result); } } From ad7f0a75e2955b97d400e72f8ca4c5d2759dba3b Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 12:14:00 +0100 Subject: [PATCH 23/28] add using alias for core constants. --- uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs index d11425a84..4b76fb953 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerContainerBase.cs @@ -25,6 +25,8 @@ using uSync.Core.Dependency; using uSync.Core.Serialization; +using CoreConstants = uSync.Core.uSyncConstants; + namespace uSync.BackOffice.SyncHandlers; /// @@ -128,15 +130,15 @@ public virtual async Task> ProcessPostImportAsync(IEnum if (_loadedFiles.TryGetValue(action.FileName, out XElement? xml) is false) _loadedFiles[action.FileName] = await syncFileService.LoadXElementAsync(action.FileName); - if (_loadedFiles[action.FileName].Name.LocalName.Equals(Core.uSyncConstants.Serialization.Empty) is true) + if (_loadedFiles[action.FileName].Name.LocalName.Equals(CoreConstants.Serialization.Empty) is true) { // single results.AddRange(await ImportSingleElementAsync(_loadedFiles[action.FileName], action.FileName, config, options)); } else { - // multiple ? - var node = _loadedFiles[action.FileName].XPathSelectElement($"//{Core.uSyncConstants.Serialization.Empty}[@Key='{action.Key}']"); + // multiple ? + var node = _loadedFiles[action.FileName].XPathSelectElement($"//{CoreConstants.Serialization.Empty}[@Key='{action.Key}']"); if (node is null) continue; results.AddRange(await ImportSingleElementAsync(node, action.FileName, config, options)); From 07e65bf607e308ecfbf3f9bb5ff7b6c9a1de6d79 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 12:15:39 +0100 Subject: [PATCH 24/28] Replace File.Exists with call to SyncFileService.FileExists --- uSync.BackOffice/Services/SyncService_Handlers.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uSync.BackOffice/Services/SyncService_Handlers.cs b/uSync.BackOffice/Services/SyncService_Handlers.cs index 810be739e..99958a7f7 100644 --- a/uSync.BackOffice/Services/SyncService_Handlers.cs +++ b/uSync.BackOffice/Services/SyncService_Handlers.cs @@ -39,7 +39,7 @@ public async Task> ReportHandlerAsync(string handler, u var folders = GetHandlerFolders(GetFolderFromOptions(options), handlerPair.Handler); var productionFile = $"{folders.Last()}.{_uSyncConfig.Settings.DefaultExtension}"; - if (File.Exists(productionFile)) + if (_syncFileService.FileExists(productionFile)) return await ReportMergedFile(productionFile, handlerPair, options); return await handlerPair.Handler.ReportAsync(folders, handlerPair.Settings, options.Callbacks?.Update); @@ -81,7 +81,7 @@ public async Task> ImportHandlerAsync(string handlerAli List results; var productionFile = $"{folders.Last()}.{_uSyncConfig.Settings.DefaultExtension}"; - if (File.Exists(productionFile)) + if (_syncFileService.FileExists(productionFile)) results = [.. await ImportMergedFile(productionFile, handlerPair, options)]; else results = [.. await handlerPair.Handler.ImportAllAsync(folders, handlerPair.Settings, options)]; From 4c76f4e4242e60d3208ccf3352eafa6140eea7a8 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 12:16:08 +0100 Subject: [PATCH 25/28] Update uSync.BackOffice/Configuration/SyncConfigService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- uSync.BackOffice/Configuration/SyncConfigService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uSync.BackOffice/Configuration/SyncConfigService.cs b/uSync.BackOffice/Configuration/SyncConfigService.cs index ec0c5ad95..e1377ec8e 100644 --- a/uSync.BackOffice/Configuration/SyncConfigService.cs +++ b/uSync.BackOffice/Configuration/SyncConfigService.cs @@ -80,7 +80,7 @@ public string[] GetFolders() case SyncFolderMode.Root: return [folders[0].TrimStart('/')]; case SyncFolderMode.Production: - return [Settings.ProductionFolder]; + return [Settings.ProductionFolder.TrimStart('/')]; default: return [.. folders.Select(x => x.TrimStart('/'))]; } From cc9df71bd8c8b3966448ebdc41086a4f95a5109d Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 12:16:36 +0100 Subject: [PATCH 26/28] typo fixes in the generated schema file. --- uSync.BackOffice.Targets/appsettings-schema.usync.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uSync.BackOffice.Targets/appsettings-schema.usync.json b/uSync.BackOffice.Targets/appsettings-schema.usync.json index 505960b18..c5acb1c13 100644 --- a/uSync.BackOffice.Targets/appsettings-schema.usync.json +++ b/uSync.BackOffice.Targets/appsettings-schema.usync.json @@ -205,7 +205,7 @@ }, "DisableNotificationSuppression": { "type": "boolean", - "description": "turns of use of the Notifications.Suppress method, so notifications\nfire after every item is imported.\n ", + "description": "turns off use of the Notifications.Suppress method, so notifications\nfire after every item is imported.\n ", "default": "true" }, "BackgroundNotifications": { From f75b4d8d48f43bf3d71809bb253f9dbf081f68fd Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 12:22:19 +0100 Subject: [PATCH 27/28] Update uSync.BackOffice/Hubs/HubClientService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- uSync.BackOffice/Hubs/HubClientService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uSync.BackOffice/Hubs/HubClientService.cs b/uSync.BackOffice/Hubs/HubClientService.cs index 6dc43e532..e39cd2836 100644 --- a/uSync.BackOffice/Hubs/HubClientService.cs +++ b/uSync.BackOffice/Hubs/HubClientService.cs @@ -86,7 +86,7 @@ public void PostUpdate(string message, int count, int total) private int _end = 0; /// - /// set a range (start to end) that we except the next set of updates to be bound within. + /// set a range (start to end) that we expect the next set of updates to be bound within. /// public void SetCountRange(int start, int end) { From 42743b7a229fea535b512344e607e798261e11a5 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Wed, 15 Oct 2025 12:22:28 +0100 Subject: [PATCH 28/28] Update uSync.BackOffice/Services/ISyncFileService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- uSync.BackOffice/Services/ISyncFileService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uSync.BackOffice/Services/ISyncFileService.cs b/uSync.BackOffice/Services/ISyncFileService.cs index 2aeaea521..839886777 100644 --- a/uSync.BackOffice/Services/ISyncFileService.cs +++ b/uSync.BackOffice/Services/ISyncFileService.cs @@ -136,7 +136,7 @@ public interface ISyncFileService Task LoadXElementAsync(string file); /// - /// merge all the files in the give folders into a single xml node, that can be bulk imported + /// merge all the files in the given folders into a single xml node, that can be bulk imported /// Task MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string fileName, string extension);