From d46ce818b98030449b81be23589435e8b60c7be0 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Tue, 3 Oct 2023 07:50:18 +0100 Subject: [PATCH] Add ability to create clean as part of export request. --- .../Configuration/uSyncHandlerSettings.cs | 8 +++++ .../Services/uSyncService_Single.cs | 31 ++++++++++++------- .../SyncHandlers/Handlers/ContentHandler.cs | 3 ++ .../SyncHandlers/Handlers/MediaHandler.cs | 3 ++ .../SyncHandlers/Interfaces/ISyncHandler.cs | 15 +++++---- .../SyncHandlers/SyncHandlerBase.cs | 15 ++++++--- .../SyncHandlers/SyncHandlerRoot.cs | 25 ++++++++++++++- 7 files changed, 76 insertions(+), 24 deletions(-) diff --git a/uSync.BackOffice/Configuration/uSyncHandlerSettings.cs b/uSync.BackOffice/Configuration/uSyncHandlerSettings.cs index d1f10da4..4c0df1d2 100644 --- a/uSync.BackOffice/Configuration/uSyncHandlerSettings.cs +++ b/uSync.BackOffice/Configuration/uSyncHandlerSettings.cs @@ -50,6 +50,14 @@ public class HandlerSettings [DefaultValue("")] public string Group { get; set; } = string.Empty; + /// + /// create a corresponding _clean file for this export + /// + /// + /// the clean file will only get created if the item in question has children. + /// + public bool CreateClean { get; set; } = false; + /// /// Additional settings for the handler /// diff --git a/uSync.BackOffice/Services/uSyncService_Single.cs b/uSync.BackOffice/Services/uSyncService_Single.cs index 92b7a25d..1fa3425b 100644 --- a/uSync.BackOffice/Services/uSyncService_Single.cs +++ b/uSync.BackOffice/Services/uSyncService_Single.cs @@ -18,12 +18,12 @@ namespace uSync.BackOffice { /// - /// Implimentation of paged import methods. + /// Implementation of paged import methods. /// public partial class uSyncService { /// - /// Peform a paged report against a given folder + /// Perform a paged report against a given folder /// public IEnumerable ReportPartial(string folder, uSyncPagedImportOptions options, out int total) { @@ -31,6 +31,9 @@ public IEnumerable ReportPartial(string folder, uSyncPagedImportOpt return ReportPartial(orderedNodes, options, out total); } + /// + /// perform a paged report with the supplied ordered nodes + /// public IEnumerable ReportPartial(IList orderedNodes, uSyncPagedImportOptions options, out int total) { total = orderedNodes.Count; @@ -53,16 +56,16 @@ public IEnumerable ReportPartial(IList orderedNode { lastType = itemType; handlerPair = _handlerFactory.GetValidHandlerByTypeName(itemType, syncHandlerOptions); + + handlerPair?.Handler.PreCacheFolderKeys(folder, orderedNodes.Select(x => x.Key).ToList()); } if (handlerPair == null) { - _logger.LogWarning("No handler was found for {alias} item might not process correctly", itemType); + _logger.LogWarning("No handler for {itemType} {alias}", itemType, item.Node.GetAlias()); continue; } - handlerPair.Handler.PreCacheFolderKeys(folder, orderedNodes.Select(x => x.Key).ToList()); - options.Callbacks?.Update.Invoke(item.Node.GetAlias(), CalculateProgress(index, total, options.ProgressMin, options.ProgressMax), 100); @@ -78,7 +81,7 @@ public IEnumerable ReportPartial(IList orderedNode } /// - /// Peform a paged Import against a given folder + /// Perform a paged Import against a given folder /// public IEnumerable ImportPartial(string folder, uSyncPagedImportOptions options, out int total) { @@ -86,6 +89,9 @@ public IEnumerable ImportPartial(string folder, uSyncPagedImportOpt return ImportPartial(orderedNodes, options, out total); } + /// + /// perform an import of items from the suppled ordered node list. + /// public IEnumerable ImportPartial(IList orderedNodes, uSyncPagedImportOptions options, out int total) { lock (_importLock) @@ -114,8 +120,8 @@ public IEnumerable ImportPartial(IList orderedNode lastType = itemType; handlerPair = _handlerFactory.GetValidHandlerByTypeName(itemType, syncHandlerOptions); - // special case, blueprints looks like IContent items, except they are slightly diffrent - // so we check for them speicifically and get the handler for the enity rather than the object type. + // special case, blueprints looks like IContent items, except they are slightly different + // so we check for them specifically and get the handler for the entity rather than the object type. if (item.Node.IsContent() && item.Node.IsBlueprint()) { lastType = UdiEntityType.DocumentBlueprint; @@ -146,7 +152,7 @@ public IEnumerable ImportPartial(IList orderedNode } /// - /// Peform a paged Import second pass against a given folder + /// Perform a paged Import second pass against a given folder /// public IEnumerable ImportPartialSecondPass(IEnumerable actions, uSyncPagedImportOptions options) { @@ -193,7 +199,7 @@ public IEnumerable ImportPartialSecondPass(IEnumerable } /// - /// Peform a paged Import post import against a given folder + /// Perform a paged Import post import against a given folder /// public IEnumerable ImportPartialPostImport(IEnumerable actions, uSyncPagedImportOptions options) { @@ -247,7 +253,7 @@ public IEnumerable ImportPartialPostImport(IEnumerable } /// - /// Peform a paged Clean after import for a given folder + /// Perform a paged Clean after import for a given folder /// public IEnumerable ImportPostCleanFiles(IEnumerable actions, uSyncPagedImportOptions options) { @@ -343,6 +349,9 @@ public OrderedNodeInfo(string filename, XElement node) /// public XElement Node { get; set; } + /// + /// the Guid key for this item, so we can cache the list of keys + /// public Guid Key { get; set; } /// diff --git a/uSync.BackOffice/SyncHandlers/Handlers/ContentHandler.cs b/uSync.BackOffice/SyncHandlers/Handlers/ContentHandler.cs index fc355f19..b698b265 100644 --- a/uSync.BackOffice/SyncHandlers/Handlers/ContentHandler.cs +++ b/uSync.BackOffice/SyncHandlers/Handlers/ContentHandler.cs @@ -58,6 +58,9 @@ public ContentHandler( this.serializer = syncItemFactory.GetSerializer("ContentSerializer"); } + protected override bool HasChildren(IContent item) + => contentService.HasChildren(item.Id); + /// /// Get child items /// diff --git a/uSync.BackOffice/SyncHandlers/Handlers/MediaHandler.cs b/uSync.BackOffice/SyncHandlers/Handlers/MediaHandler.cs index 7498360b..a4304a3b 100644 --- a/uSync.BackOffice/SyncHandlers/Handlers/MediaHandler.cs +++ b/uSync.BackOffice/SyncHandlers/Handlers/MediaHandler.cs @@ -51,6 +51,9 @@ public MediaHandler( this.mediaService = mediaService; } + protected override bool HasChildren(IMedia item) + => mediaService.HasChildren(item.Id); + /// protected override IEnumerable GetChildItems(IEntity parent) { diff --git a/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs b/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs index d7af0ae4..88c252f4 100644 --- a/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs +++ b/uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs @@ -76,7 +76,7 @@ public interface ISyncHandler string EntityType { get; } /// - /// The type name of the items hanled (Item.getType().ToString()) + /// The type name of the items handled (Item.getType().ToString()) /// string TypeName { get; } @@ -95,7 +95,7 @@ public interface ISyncHandler /// /// folder to use when exporting /// Handler settings to use for export - /// Callbacks to keep UI uptodate + /// Callbacks to keep UI upto date /// List of actions detailing changes IEnumerable ExportAll(string folder, HandlerSettings settings, SyncUpdateCallback callback); @@ -124,7 +124,7 @@ public interface ISyncHandler /// folder to use when Importing /// Handler settings to use for import /// Force the import even if the settings haven't changed - /// Callbacks to keep UI uptodate + /// Callbacks to keep UI upto date /// List of actions detailing changes IEnumerable ImportAll(string folder, HandlerSettings settings, bool force, SyncUpdateCallback callback); @@ -138,7 +138,7 @@ public interface ISyncHandler /// /// folder to use when reporting /// Handler settings to use for report - /// Callbacks to keep UI uptodate + /// Callbacks to keep UI upto date /// List of actions detailing changes IEnumerable Report(string folder, HandlerSettings settings, SyncUpdateCallback callback); @@ -154,12 +154,12 @@ public interface ISyncHandler IEnumerable ImportSecondPass(uSyncAction action, HandlerSettings settings, uSyncImportOptions options); /// - /// default impimentation, roothandler does do this. + /// default implementation, root handler does do this. /// Udi FindFromNode(XElement node) => null; /// - /// is this a current node (roothandler can do this too) + /// is this a current node (root handler can do this too) /// ChangeType GetItemStatus(XElement node) => ChangeType.NoChange; @@ -169,7 +169,6 @@ public interface ISyncHandler /// /// /// - void PreCacheFolderKeys(string folder, IList keys) - { } + void PreCacheFolderKeys(string folder, IList keys) { } } } diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerBase.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerBase.cs index 94f4e3c9..ff927d5e 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerBase.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerBase.cs @@ -12,6 +12,7 @@ using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; +using Umbraco.Extensions; using uSync.BackOffice.Configuration; using uSync.BackOffice.Services; @@ -53,6 +54,9 @@ public SyncHandlerBase( this.entityService = entityService; } + protected override bool HasChildren(TObject item) + => entityService.GetChildren(item.Id).Any(); + /// /// given a folder we calculate what items we can remove, becuase they are /// not in one the the files in the folder. @@ -164,7 +168,12 @@ protected override IEnumerable GetChildItems(IEntity parent) /// virtual protected IEnumerable GetChildItems(int parent) { - if (this.itemObjectType != UmbracoObjectTypes.Unknown) + if (this.itemObjectType == UmbracoObjectTypes.Unknown) + return Enumerable.Empty(); + + var cacheKey = $"{GetCacheKeyBase()}_parent_{parent}"; + + return runtimeCache.GetCacheItem(cacheKey, () => { if (parent == -1) { @@ -176,9 +185,7 @@ virtual protected IEnumerable GetChildItems(int parent) // load it, so GetChildren without the object type is quicker. return entityService.GetChildren(parent); } - } - - return Enumerable.Empty(); + }, null); } /// diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs index 80997b6d..809eae14 100644 --- a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs +++ b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs @@ -864,6 +864,11 @@ virtual public IEnumerable Export(TObject item, string folder, Hand { // only write the file to disk if it should be exported. syncFileService.SaveXElement(attempt.Item, filename); + + if (config.CreateClean && HasChildren(item)) + { + CreateCleanFile(GetItemKey(item), filename); + } } else { @@ -876,6 +881,24 @@ virtual public IEnumerable Export(TObject item, string folder, Hand return uSyncActionHelper.SetAction(attempt, filename, GetItemKey(item), this.Alias).AsEnumerableOfOne(); } + protected virtual bool HasChildren(TObject item) + => true; + + private void CreateCleanFile(Guid key, string filename) + { + if (string.IsNullOrWhiteSpace(filename) || key == Guid.Empty) + return; + + var folder = Path.GetDirectoryName(filename); + var name = Path.GetFileNameWithoutExtension(filename); + + var cleanPath = Path.Combine(folder, $"{name}_clean.config"); + + var node = XElementExtensions.MakeEmpty(key, SyncActionType.Clean, $"clean {name} children"); + node.Add(new XAttribute("itemType", serializer.ItemType)); + syncFileService.SaveXElement(node, cleanPath); + } + #endregion #region Reporting @@ -1760,7 +1783,7 @@ private string GetNameFromFileOrNode(string filename, XElement node) => !string.IsNullOrWhiteSpace(filename) ? filename : node.GetAlias(); - private string GetCacheKeyBase() + protected string GetCacheKeyBase() => $"keycache_{this.Alias}_{Thread.CurrentThread.ManagedThreadId}"; private string PrepCaches()