diff --git a/uSync.BackOffice/Configuration/uSyncSettings.cs b/uSync.BackOffice/Configuration/uSyncSettings.cs
index d5661fc68..79ab064ae 100644
--- a/uSync.BackOffice/Configuration/uSyncSettings.cs
+++ b/uSync.BackOffice/Configuration/uSyncSettings.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.ComponentModel;
+using System.Text.Json.Serialization;
namespace uSync.BackOffice.Configuration;
@@ -97,15 +98,15 @@ public class uSyncSettings
///
/// Export when an item is saved in Umbraco
///
- [DefaultValue("All")]
- public string ExportOnSave { get; set; } = "All";
+ [DefaultValue(uSync.EverythingGroupName)]
+ public string ExportOnSave { get; set; } = uSync.EverythingGroupName;
///
/// The handler groups that are enabled in the UI.
///
- [DefaultValue("All")]
- public string UIEnabledGroups { get; set; } = "All";
+ [DefaultValue(uSync.EverythingGroupName)]
+ public string UIEnabledGroups { get; set; } = uSync.EverythingGroupName;
///
/// Debug reports (creates an export into a temp folder for comparison)
@@ -176,8 +177,8 @@ public class uSyncSettings
///
/// Handler group(s) to run on first boot, default is All (so full import)
///
- [DefaultValue("All")]
- public string FirstBootGroup { get; set; } = "All";
+ [DefaultValue(uSync.EverythingGroupName)]
+ public string FirstBootGroup { get; set; } = uSync.EverythingGroupName;
///
/// Disable the default dashboard (so people can't accidently press the buttons).
@@ -264,6 +265,7 @@ public class uSyncSettings
///
/// uSync's folder mode - normal, root or production
///
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum SyncFolderMode
{
///
@@ -285,6 +287,7 @@ public enum SyncFolderMode
///
/// Mode is where the processing happens - normal or background
///
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum SyncProcessingMode
{
///
diff --git a/uSync.BackOffice/Extensions/uSyncActionExtensions.cs b/uSync.BackOffice/Extensions/uSyncActionExtensions.cs
index a32027a21..8c29aad9c 100644
--- a/uSync.BackOffice/Extensions/uSyncActionExtensions.cs
+++ b/uSync.BackOffice/Extensions/uSyncActionExtensions.cs
@@ -41,7 +41,7 @@ public static int CountChanges(this IEnumerable actions)
public static bool IsValidAction(this HandlerActions requestedAction, IEnumerable actions)
=> requestedAction == HandlerActions.None ||
!actions.Any() ||
- actions.InvariantContains("all") ||
+ actions.InvariantContains(uSync.EverythingGroupName) ||
actions.InvariantContains(requestedAction.ToString());
///
diff --git a/uSync.BackOffice/Models/SyncActionOptions.cs b/uSync.BackOffice/Models/SyncActionOptions.cs
index 18cca1f87..1350ab953 100644
--- a/uSync.BackOffice/Models/SyncActionOptions.cs
+++ b/uSync.BackOffice/Models/SyncActionOptions.cs
@@ -29,6 +29,12 @@ public class SyncActionOptions
///
public string? Set { get; set; }
+
+ ///
+ /// the group within the set that is being processed - e.g settings, content, media etc.
+ ///
+ public string? Group { get; set; }
+
///
/// SyncActions to use as the source for all individual actions
///
diff --git a/uSync.BackOffice/Notifications/uSyncNotifications/CancelableuSyncBulkNotification.cs b/uSync.BackOffice/Notifications/uSyncNotifications/CancelableuSyncBulkNotification.cs
index 039d0c1a2..e160dba79 100644
--- a/uSync.BackOffice/Notifications/uSyncNotifications/CancelableuSyncBulkNotification.cs
+++ b/uSync.BackOffice/Notifications/uSyncNotifications/CancelableuSyncBulkNotification.cs
@@ -13,7 +13,7 @@ public class CancelableuSyncBulkNotification : uSyncBulkNotification, ICancelabl
/// Notification constructor
///
public CancelableuSyncBulkNotification()
- : base(Enumerable.Empty())
+ : base(Enumerable.Empty(), null)
{ }
///
diff --git a/uSync.BackOffice/Notifications/uSyncNotifications/uSyncBulkNotification.cs b/uSync.BackOffice/Notifications/uSyncNotifications/uSyncBulkNotification.cs
index 813a9677d..2cef06642 100644
--- a/uSync.BackOffice/Notifications/uSyncNotifications/uSyncBulkNotification.cs
+++ b/uSync.BackOffice/Notifications/uSyncNotifications/uSyncBulkNotification.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using Umbraco.Cms.Core.Notifications;
@@ -13,11 +14,23 @@ public class uSyncBulkNotification : INotification
/// generate new BulkNotificationObject
///
///
+ public uSyncBulkNotification(IEnumerable actions, string? group)
+ {
+ this.Group = group;
+ this.Actions = actions;
+ }
+
+ [Obsolete("Use the constructor with group and actions instead - will be removed in v19")]
public uSyncBulkNotification(IEnumerable actions)
{
this.Actions = actions;
}
+ ///
+ /// The group e.g settings, content that this action ran under.
+ ///
+ public string? Group { get; set; }
+
///
/// actions that have occured during the bulk operation
///
diff --git a/uSync.BackOffice/Notifications/uSyncNotifications/uSyncExportCompletedNotification.cs b/uSync.BackOffice/Notifications/uSyncNotifications/uSyncExportCompletedNotification.cs
index c2d6b3c17..add72f1a3 100644
--- a/uSync.BackOffice/Notifications/uSyncNotifications/uSyncExportCompletedNotification.cs
+++ b/uSync.BackOffice/Notifications/uSyncNotifications/uSyncExportCompletedNotification.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
namespace uSync.BackOffice;
@@ -7,8 +8,12 @@ namespace uSync.BackOffice;
///
public class uSyncExportCompletedNotification : uSyncBulkNotification
{
+ ///
+ public uSyncExportCompletedNotification(IEnumerable actions, string? group)
+ : base(actions, group) { }
///
+ [Obsolete("Use the constructor with the group parameter instead will be removed in v19")]
public uSyncExportCompletedNotification(IEnumerable actions)
- : base(actions) { }
+ : base(actions, null) { }
}
diff --git a/uSync.BackOffice/Notifications/uSyncNotifications/uSyncImportCompletedNotification.cs b/uSync.BackOffice/Notifications/uSyncNotifications/uSyncImportCompletedNotification.cs
index 9adf64b19..e50dfb8f4 100644
--- a/uSync.BackOffice/Notifications/uSyncNotifications/uSyncImportCompletedNotification.cs
+++ b/uSync.BackOffice/Notifications/uSyncNotifications/uSyncImportCompletedNotification.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
namespace uSync.BackOffice;
@@ -7,7 +8,13 @@ namespace uSync.BackOffice;
///
public class uSyncImportCompletedNotification : uSyncBulkNotification
{
+
+ ///
+ public uSyncImportCompletedNotification(IEnumerable actions, string? group)
+ : base(actions, group) { }
+
///
+ [Obsolete("Use the constructor with the group parameter instead will be removed in v19")]
public uSyncImportCompletedNotification(IEnumerable actions)
- : base(actions) { }
+ : base(actions, null) { }
}
diff --git a/uSync.BackOffice/Notifications/uSyncNotifications/uSyncReportCompletedNotification.cs b/uSync.BackOffice/Notifications/uSyncNotifications/uSyncReportCompletedNotification.cs
index a361483e2..0846e9b77 100644
--- a/uSync.BackOffice/Notifications/uSyncNotifications/uSyncReportCompletedNotification.cs
+++ b/uSync.BackOffice/Notifications/uSyncNotifications/uSyncReportCompletedNotification.cs
@@ -7,7 +7,12 @@ namespace uSync.BackOffice;
///
public class uSyncReportCompletedNotification : uSyncBulkNotification
{
+ ///
+ public uSyncReportCompletedNotification(IEnumerable actions, string? group )
+ : base(actions, group) { }
+
+
///
public uSyncReportCompletedNotification(IEnumerable actions)
- : base(actions) { }
+ : base(actions, null) { }
}
diff --git a/uSync.BackOffice/Services/ISyncService.cs b/uSync.BackOffice/Services/ISyncService.cs
index 9ccce0492..84937fae2 100644
--- a/uSync.BackOffice/Services/ISyncService.cs
+++ b/uSync.BackOffice/Services/ISyncService.cs
@@ -152,14 +152,20 @@ public interface ISyncService
///
Task StartBulkProcessAsync(HandlerActions action);
+
///
/// trigger the end of the bulk process
///
+ [Obsolete("Use StartBulkProcessAsync(HandlerActions action, string group) instead")]
Task FinishBulkProcessAsync(HandlerActions action, IEnumerable actions);
+ ///
+ /// trigger the end of the bulk process
+ ///
+ Task FinishBulkProcessAsync(HandlerActions action, string? group, IEnumerable actions);
+
///
/// merge the given folders in single 'production' files for each handler.
///
Task MergeExportFolder(string[] paths, IEnumerable handlers);
-
}
\ No newline at end of file
diff --git a/uSync.BackOffice/Services/SyncActionService.cs b/uSync.BackOffice/Services/SyncActionService.cs
index 988b14f15..def996b78 100644
--- a/uSync.BackOffice/Services/SyncActionService.cs
+++ b/uSync.BackOffice/Services/SyncActionService.cs
@@ -206,7 +206,7 @@ public async Task StartProcessAsync(HandlerActions action)
///
public async Task FinishProcessAsync(SyncFinalActionRequest request)
{
- await _uSyncService.FinishBulkProcessAsync(request.HandlerAction, request.Actions);
+ await _uSyncService.FinishBulkProcessAsync(request.HandlerAction, request.ActionOptions.Group, request.Actions);
_timer?.Stop();
var elapsed = _timer?.ElapsedMilliseconds ?? 0;
diff --git a/uSync.BackOffice/Services/SyncService.cs b/uSync.BackOffice/Services/SyncService.cs
index 6332db6be..3f5dd0f96 100644
--- a/uSync.BackOffice/Services/SyncService.cs
+++ b/uSync.BackOffice/Services/SyncService.cs
@@ -205,7 +205,7 @@ public async Task> ImportAsync(string[] folders, bool f
var results = await SyncService.PerformPostImportAsync(handlers, actions);
// fire complete
- await _mutexService.FireBulkCompleteAsync(new uSyncImportCompletedNotification(actions));
+ await _mutexService.FireBulkCompleteAsync(new uSyncImportCompletedNotification(actions, handlerOptions.Group));
_logger.LogInformation("uSync Import: {handlerCount} handlers, processed {itemCount} items, {changeCount} changes in {ElapsedMilliseconds}ms",
handlers.Count(),
@@ -402,7 +402,7 @@ public async Task> ExportAsync(string folder, IEnumerab
summary.UpdateMessage("Export Completed");
callbacks?.Callback?.Invoke(summary);
- await _mutexService.FireBulkCompleteAsync(new uSyncExportCompletedNotification(actions));
+ await _mutexService.FireBulkCompleteAsync(new uSyncExportCompletedNotification(actions, null));
sw.Stop();
diff --git a/uSync.BackOffice/Services/SyncService_Handlers.cs b/uSync.BackOffice/Services/SyncService_Handlers.cs
index 99958a7f7..60886d403 100644
--- a/uSync.BackOffice/Services/SyncService_Handlers.cs
+++ b/uSync.BackOffice/Services/SyncService_Handlers.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
@@ -154,19 +155,24 @@ public async Task StartBulkProcessAsync(HandlerActions action)
}
/// >
+ [Obsolete("Use the overload with the group parameter instead")]
public async Task FinishBulkProcessAsync(HandlerActions action, IEnumerable actions)
+ => await FinishBulkProcessAsync(action, uSync.EverythingGroupName, actions);
+
+ /// >
+ public async Task FinishBulkProcessAsync(HandlerActions action, string? group, IEnumerable actions)
{
switch (action)
{
case HandlerActions.Export:
await WriteVersionFileAsync(_uSyncConfig.GetWorkingFolder());
- await _mutexService.FireBulkCompleteAsync(new uSyncExportCompletedNotification(actions));
+ await _mutexService.FireBulkCompleteAsync(new uSyncExportCompletedNotification(actions, group));
break;
case HandlerActions.Import:
- await _mutexService.FireBulkCompleteAsync(new uSyncImportCompletedNotification(actions));
+ await _mutexService.FireBulkCompleteAsync(new uSyncImportCompletedNotification(actions, group));
break;
case HandlerActions.Report:
- await _mutexService.FireBulkCompleteAsync(new uSyncReportCompletedNotification(actions));
+ await _mutexService.FireBulkCompleteAsync(new uSyncReportCompletedNotification(actions, group));
break;
}
}
diff --git a/uSync.BackOffice/SyncHandlers/Models/HandlerActionNames.cs b/uSync.BackOffice/SyncHandlers/Models/HandlerActionNames.cs
index 2282081e0..7c5d4e406 100644
--- a/uSync.BackOffice/SyncHandlers/Models/HandlerActionNames.cs
+++ b/uSync.BackOffice/SyncHandlers/Models/HandlerActionNames.cs
@@ -1,10 +1,12 @@
using System;
+using System.Text.Json.Serialization;
namespace uSync.BackOffice.SyncHandlers.Models;
///
/// Possible actions a handler can do (stored in config)
///
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum HandlerActions
{
///
@@ -40,7 +42,7 @@ public enum HandlerActions
///
/// All actions
///
- [SyncActionName("All")]
+ [SyncActionName(uSync.EverythingGroupName)]
All
}
diff --git a/uSync.BackOffice/SyncHandlers/Models/HandlerConfigPair.cs b/uSync.BackOffice/SyncHandlers/Models/HandlerConfigPair.cs
index 0f2957a31..1f02e983e 100644
--- a/uSync.BackOffice/SyncHandlers/Models/HandlerConfigPair.cs
+++ b/uSync.BackOffice/SyncHandlers/Models/HandlerConfigPair.cs
@@ -38,7 +38,7 @@ public static bool IsEnabled(this HandlerConfigPair handlerAndConfig)
public static bool IsValidGroup(this HandlerConfigPair handlerAndConfig, string group)
{
// empty means all as does 'all'
- if (string.IsNullOrWhiteSpace(group) || group.InvariantEquals("all")) return true;
+ if (string.IsNullOrWhiteSpace(group) || group.InvariantEquals(uSync.EverythingGroupName)) return true;
var handlerGroup = handlerAndConfig.Handler.Group;
if (!string.IsNullOrWhiteSpace(handlerAndConfig.Settings.Group))
diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs
index c32451040..38b0fe0ce 100644
--- a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs
+++ b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs
@@ -1370,7 +1370,7 @@ protected bool ShouldProcessEvent()
var group = !string.IsNullOrWhiteSpace(DefaultConfig.Group) ? DefaultConfig.Group : this.Group;
- if (uSyncConfig.Settings.ExportOnSave.InvariantContains("All") ||
+ if (uSyncConfig.Settings.ExportOnSave.InvariantContains(uSync.EverythingGroupName) ||
uSyncConfig.Settings.ExportOnSave.InvariantContains(group))
{
return HandlerActions.Save.IsValidAction(DefaultConfig.Actions);
diff --git a/uSync.BackOffice/Tracker/ISyncTrackerService.cs b/uSync.BackOffice/Tracker/ISyncTrackerService.cs
new file mode 100644
index 000000000..f8f108ab9
--- /dev/null
+++ b/uSync.BackOffice/Tracker/ISyncTrackerService.cs
@@ -0,0 +1,10 @@
+using System;
+using System.Threading.Tasks;
+
+namespace uSync.BackOffice.Tracker;
+
+public interface ISyncTrackerService
+{
+ Task GetLastSync(string group);
+ Task SaveLastSync(string group);
+}
\ No newline at end of file
diff --git a/uSync.BackOffice/Tracker/SyncTrackerBuilderExtensions.cs b/uSync.BackOffice/Tracker/SyncTrackerBuilderExtensions.cs
new file mode 100644
index 000000000..2fbd36adb
--- /dev/null
+++ b/uSync.BackOffice/Tracker/SyncTrackerBuilderExtensions.cs
@@ -0,0 +1,15 @@
+using Microsoft.Extensions.DependencyInjection;
+
+using Umbraco.Cms.Core.DependencyInjection;
+
+namespace uSync.BackOffice.Tracker;
+
+internal static class SyncTrackerBuilderExtensions
+{
+ public static IUmbracoBuilder AddSyncTracker(this IUmbracoBuilder builder)
+ {
+ builder.Services.AddSingleton();
+ builder.AddNotificationAsyncHandler();
+ return builder;
+ }
+}
diff --git a/uSync.BackOffice/Tracker/SyncTrackerNotificationHandler.cs b/uSync.BackOffice/Tracker/SyncTrackerNotificationHandler.cs
new file mode 100644
index 000000000..b3ad51019
--- /dev/null
+++ b/uSync.BackOffice/Tracker/SyncTrackerNotificationHandler.cs
@@ -0,0 +1,22 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+using Umbraco.Cms.Core.Events;
+
+namespace uSync.BackOffice.Tracker;
+
+internal class SyncTrackerNotificationHandler
+ : INotificationAsyncHandler
+{
+ private readonly ISyncTrackerService _syncTrackerService;
+
+ public SyncTrackerNotificationHandler(ISyncTrackerService syncTrackerService)
+ {
+ _syncTrackerService = syncTrackerService;
+ }
+
+ public async Task HandleAsync(uSyncImportCompletedNotification notification, CancellationToken cancellationToken)
+ {
+ await _syncTrackerService.SaveLastSync(notification.Group ?? uSync.EverythingGroupName);
+ }
+}
diff --git a/uSync.BackOffice/Tracker/SyncTrackerService.cs b/uSync.BackOffice/Tracker/SyncTrackerService.cs
new file mode 100644
index 000000000..28de5f55a
--- /dev/null
+++ b/uSync.BackOffice/Tracker/SyncTrackerService.cs
@@ -0,0 +1,64 @@
+using Microsoft.Extensions.Logging;
+
+using System;
+using System.Threading.Tasks;
+
+using Umbraco.Cms.Core.Services;
+
+namespace uSync.BackOffice.Tracker;
+
+
+internal class SyncTrackerService : ISyncTrackerService
+{
+ const string _keyPrefix = "uSync.SyncTracker.";
+
+ private readonly ILogger _logger;
+ private readonly IKeyValueService _keyValueService;
+
+ public SyncTrackerService(IKeyValueService keyValueService, ILogger logger)
+ {
+ _keyValueService = keyValueService;
+ _logger = logger;
+ }
+
+ public Task SaveLastSync(string group)
+ {
+ try
+ {
+ var key = $"{_keyPrefix}{group}";
+ _keyValueService.SetValue(key, DateTime.UtcNow.ToString("o"));
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Error saving last sync time for group {Group}", group);
+ }
+ return Task.CompletedTask;
+
+ }
+
+ public async Task GetLastSync(string group)
+ {
+ var lastSync = await InternalGetLastSync(group);
+ var lastAllSync = await InternalGetLastSync(uSync.EverythingGroupName);
+ return lastSync > lastAllSync ? lastSync : lastAllSync;
+ }
+
+ private Task InternalGetLastSync(string group)
+ {
+ try
+ {
+ var key = $"{_keyPrefix}{group}";
+ var value = _keyValueService.GetValue(key);
+ if (DateTime.TryParse(value, null, System.Globalization.DateTimeStyles.RoundtripKind, out var result))
+ {
+ return Task.FromResult(result);
+ }
+ }
+ catch(Exception ex)
+ {
+ _logger.LogWarning(ex, "Error retrieving last sync time for group {Group}", group);
+ }
+
+ return Task.FromResult(null);
+ }
+}
diff --git a/uSync.BackOffice/uSyncBackOffice.cs b/uSync.BackOffice/uSyncBackOffice.cs
index 5fdc9ae8b..b28790257 100644
--- a/uSync.BackOffice/uSyncBackOffice.cs
+++ b/uSync.BackOffice/uSyncBackOffice.cs
@@ -24,6 +24,11 @@ public class uSync
///
public const string EventPausedKey = "uSync.PausedKey";
+ ///
+ /// the name we use internally for the 'everything' group.
+ ///
+ public const string EverythingGroupName = "All";
+
internal class Trees
{
internal const string uSync = "usync";
diff --git a/uSync.BackOffice/uSyncBackOfficeBuilderExtensions.cs b/uSync.BackOffice/uSyncBackOfficeBuilderExtensions.cs
index eefa99efe..cf7b6513f 100644
--- a/uSync.BackOffice/uSyncBackOfficeBuilderExtensions.cs
+++ b/uSync.BackOffice/uSyncBackOfficeBuilderExtensions.cs
@@ -25,6 +25,7 @@
using uSync.BackOffice.SyncHandlers;
using uSync.BackOffice.SyncHandlers.Handlers;
using uSync.BackOffice.SyncHandlers.Interfaces;
+using uSync.BackOffice.Tracker;
using uSync.Core;
namespace uSync.BackOffice;
@@ -89,6 +90,8 @@ public static IUmbracoBuilder AdduSync(this IUmbracoBuilder builder, Action();
builder.Services.AddSingleton();
diff --git a/uSync.Backoffice.Management.Api/Controllers/Actions/uSyncActionsController.cs b/uSync.Backoffice.Management.Api/Controllers/Actions/uSyncActionsController.cs
index b4bfb55ae..b0229b7e7 100644
--- a/uSync.Backoffice.Management.Api/Controllers/Actions/uSyncActionsController.cs
+++ b/uSync.Backoffice.Management.Api/Controllers/Actions/uSyncActionsController.cs
@@ -32,7 +32,7 @@ public async Task> GetActions()
[ProducesResponseType(typeof(List), 200)]
public async Task> GetActionsBySet(string setName)
{
- return await Task.FromResult(_syncManagementService.GetActions(setName));
+ return await _syncManagementService.GetActionsAsync(setName);
}
diff --git a/uSync.Backoffice.Management.Api/Models/SyncActionGroup.cs b/uSync.Backoffice.Management.Api/Models/SyncActionGroup.cs
index b21fa47e2..5cfb1fcde 100644
--- a/uSync.Backoffice.Management.Api/Models/SyncActionGroup.cs
+++ b/uSync.Backoffice.Management.Api/Models/SyncActionGroup.cs
@@ -6,6 +6,7 @@ public class SyncActionGroup
public string Icon { get; set; } = "icon-box";
public string GroupName { get; set; } = "settings";
public List Buttons { get; set; } = [];
+ public DateTime? LastSync { get; set; }
}
diff --git a/uSync.Backoffice.Management.Api/Services/ISyncManagementService.cs b/uSync.Backoffice.Management.Api/Services/ISyncManagementService.cs
index 0240959a5..f67c4a0b8 100644
--- a/uSync.Backoffice.Management.Api/Services/ISyncManagementService.cs
+++ b/uSync.Backoffice.Management.Api/Services/ISyncManagementService.cs
@@ -12,7 +12,12 @@ public interface ISyncManagementService
[Obsolete("Use GetActions(string setName) instead, this will be removed in v18")]
List GetActions();
+
+ [Obsolete("Use GetActionsAsync(string setName) instead, this will be removed in v19")]
List GetActions(string setName);
+
+ Task> GetActionsAsync(string setName);
+
Func> GetHandlerMethodAsync(HandlerActions action);
Task PerformActionAsync(PerformActionRequest actionRequest, IUser? user);
diff --git a/uSync.Backoffice.Management.Api/Services/uSyncManagementService.cs b/uSync.Backoffice.Management.Api/Services/uSyncManagementService.cs
index 932386b43..c4527274e 100644
--- a/uSync.Backoffice.Management.Api/Services/uSyncManagementService.cs
+++ b/uSync.Backoffice.Management.Api/Services/uSyncManagementService.cs
@@ -13,6 +13,7 @@
using uSync.BackOffice.Services;
using uSync.BackOffice.SyncHandlers;
using uSync.BackOffice.SyncHandlers.Models;
+using uSync.BackOffice.Tracker;
namespace uSync.Backoffice.Management.Api.Services;
@@ -30,13 +31,16 @@ internal class uSyncManagementService : ISyncManagementService
private readonly ILongRunningOperationService _longRunningOperationService;
+ private readonly ISyncTrackerService _syncTrackerService;
+
public uSyncManagementService(
ISyncActionService syncActionService,
ISyncConfigService configService,
ISyncManagementCache syncManagementCache,
IHubContext hubContext,
ISyncHandlerFactory handlerFactory,
- ILongRunningOperationService longRunningOperationService)
+ ILongRunningOperationService longRunningOperationService,
+ ISyncTrackerService syncTrackerService)
{
_syncActionService = syncActionService;
_configService = configService;
@@ -44,19 +48,22 @@ public uSyncManagementService(
_hubContext = hubContext;
_handlerFactory = handlerFactory;
_longRunningOperationService = longRunningOperationService;
+ _syncTrackerService = syncTrackerService;
}
[Obsolete("Use GetActions(string setName) instead, this will be removed in v18")]
public List GetActions()
=> GetActions(_configService.Settings.DefaultSet);
+ [Obsolete("Use GetActionsAsync(string setName) instead, this will be removed in v19")]
+ public List GetActions(string setName)
+ => GetActionsAsync(setName).Result;
+
///
/// Gets the list of available actions
///
- public List GetActions(string setName)
+ public async Task> GetActionsAsync(string setName)
{
- // TODO: Load the actions based on the handlers, and the config, (so they can be turned on and off)
-
var defaultReport = new SyncActionButton()
{
Key = HandlerActions.Report.ToString(),
@@ -128,7 +135,6 @@ public List GetActions(string setName)
]
};
- // TODO: we need to load in additional action groups as needed from plugins.
List defaultButtons = [defaultReport, defaultImport, defaultExport];
List everythingButtons = [defaultReport, everythingImport, everythingExport];
@@ -143,24 +149,29 @@ public List GetActions(string setName)
foreach (var group in groups)
{
+ var groupAlias = group.Key.ToLowerInvariant();
+
actionGroups.Add(new SyncActionGroup
{
GroupName = $"{group.Key}",
Icon = group.Value,
Key = group.Key.ToLowerInvariant(),
- Buttons = defaultButtons
+ Buttons = defaultButtons,
+ LastSync = await _syncTrackerService.GetLastSync(groupAlias)
});
}
if (string.IsNullOrWhiteSpace(_configService.Settings.UIEnabledGroups) ||
- _configService.Settings.UIEnabledGroups.InvariantContains("all"))
+ _configService.Settings.UIEnabledGroups.InvariantContains(BackOffice.uSync.EverythingGroupName))
{
actionGroups.Add(new SyncActionGroup
{
GroupName = "Everything",
Icon = "icon-paper-plane-alt",
- Key = "all",
- Buttons = everythingButtons
+ Key = BackOffice.uSync.EverythingGroupName,
+ Buttons = everythingButtons,
+ LastSync = await _syncTrackerService.GetLastSync(BackOffice.uSync.EverythingGroupName)
+
});
}
@@ -247,6 +258,7 @@ await _syncActionService.StartProcessAsync(new SyncStartActionRequest
{
Folders = _configService.GetFolders(),
Set = actionRequest.Options?.Set ?? _configService.Settings.DefaultSet,
+ Group = actionRequest.Options?.Group ?? "all",
Force = actionRequest.Options?.Force ?? false,
Actions = [],
};
diff --git a/uSync.Backoffice.Management.Client/usync-assets/openapi-ts.config.ts b/uSync.Backoffice.Management.Client/usync-assets/openapi-ts.config.ts
index 3ab25f3cc..d4cb6781b 100644
--- a/uSync.Backoffice.Management.Client/usync-assets/openapi-ts.config.ts
+++ b/uSync.Backoffice.Management.Client/usync-assets/openapi-ts.config.ts
@@ -2,7 +2,7 @@ import { defineConfig } from '@hey-api/openapi-ts';
import { defaultPlugins } from '@hey-api/openapi-ts';
export default defineConfig({
- input: 'http://localhost:28580/umbraco/swagger/uSync/swagger.json',
+ input: 'http://localhost:16903/umbraco/swagger/uSync/swagger.json',
output: {
format: 'prettier',
path: 'src/api',
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/api/client.gen.ts b/uSync.Backoffice.Management.Client/usync-assets/src/api/client.gen.ts
index 13ad9c9e0..3aceefc03 100644
--- a/uSync.Backoffice.Management.Client/usync-assets/src/api/client.gen.ts
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/api/client.gen.ts
@@ -14,6 +14,6 @@ import type { ClientOptions as ClientOptions2 } from './types.gen';
export type CreateClientConfig = (override?: Config) => Config & T>;
export const client = createClient(createConfig({
- baseUrl: 'http://localhost:28580',
+ baseUrl: 'http://localhost:16903',
throwOnError: true
}));
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/api/types.gen.ts b/uSync.Backoffice.Management.Client/usync-assets/src/api/types.gen.ts
index 1435410d0..b92ef7618 100644
--- a/uSync.Backoffice.Management.Client/usync-assets/src/api/types.gen.ts
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/api/types.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
export type ClientOptions = {
- baseUrl: 'http://localhost:28580' | (string & {});
+ baseUrl: 'http://localhost:16903' | (string & {});
};
export type Assembly = {
@@ -553,6 +553,7 @@ export type SyncActionGroup = {
icon: string;
groupName: string;
buttons: Array;
+ lastSync?: string | null;
};
export enum SyncFolderMode {
@@ -866,6 +867,7 @@ export type USyncSettings = {
lockRoot: boolean;
stopFile: string;
onceFile: string;
+ ignoreStopIfOnceExists: boolean;
lockRootTypes: Array;
backgroundStartup: boolean;
defaultSet: string;
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/components/usync-action-box.ts b/uSync.Backoffice.Management.Client/usync-assets/src/components/usync-action-box.ts
index 666bd8b8d..2177a75bb 100644
--- a/uSync.Backoffice.Management.Client/usync-assets/src/components/usync-action-box.ts
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/components/usync-action-box.ts
@@ -1,23 +1,23 @@
import {
- LitElement,
customElement,
html,
css,
property,
ifDefined,
} from '@umbraco-cms/backoffice/external/lit';
-import { SyncActionGroup } from '@jumoo/uSync';
+import { SyncActionGroup, uSyncTimeFormatOptions } from '@jumoo/uSync';
import { UUIButtonState } from '@umbraco-cms/backoffice/external/uui';
import {
uSyncActionButtonClickEvent,
uSyncActionPerformEvent,
} from '../workspace/components/events';
+import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
/**
* displays the action buttons for a given group
*/
@customElement('usync-action-box')
-export class uSyncActionBox extends LitElement {
+export class uSyncActionBox extends UmbLitElement {
/**
* Collection of buttons to display.
*/
@@ -62,7 +62,9 @@ export class uSyncActionBox extends LitElement {
return html`
-
${this.group?.groupName}
+
+ ${this.group?.groupName}
+
${dropdownButtons}
@@ -70,6 +72,11 @@ export class uSyncActionBox extends LitElement {
`;
}
+ getLastSyncDate() {
+ if (!this.group?.lastSync) return '';
+ return `Last imported: ${this.localize.date(this.group.lastSync, uSyncTimeFormatOptions)}`;
+ }
+
static styles = css`
:host {
flex-grow: 1;
@@ -86,7 +93,8 @@ export class uSyncActionBox extends LitElement {
}
.box-heading {
- font-size: var(--uui-size-8);
+ font-size: var(--uui-type-h3-size);
+ cursor: help;
margin: 0;
}
@@ -108,6 +116,12 @@ export class uSyncActionBox extends LitElement {
.disabled {
opacity: 0.4;
}
+
+ .last-sync {
+ color: var(--uui-color-text-alt);
+ font-style: italic;
+ font-size: var(--uui-size-5);
+ }
`;
}
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/constants.ts b/uSync.Backoffice.Management.Client/usync-assets/src/constants.ts
index da9985f48..ea33026c9 100644
--- a/uSync.Backoffice.Management.Client/usync-assets/src/constants.ts
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/constants.ts
@@ -34,3 +34,12 @@ const _constants = {
};
export const uSyncConstants = _constants;
+
+export const uSyncTimeFormatOptions: Intl.DateTimeFormatOptions = {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ second: 'numeric',
+};
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/workspace/views/default/default.element.ts b/uSync.Backoffice.Management.Client/usync-assets/src/workspace/views/default/default.element.ts
index f1667f57d..0343c5bca 100644
--- a/uSync.Backoffice.Management.Client/usync-assets/src/workspace/views/default/default.element.ts
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/workspace/views/default/default.element.ts
@@ -353,8 +353,9 @@ export class uSyncDefaultViewElement extends UmbLitElement {
}
.action-buttons-box {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(430px, 1fr));
position: relative;
- display: flex;
gap: var(--uui-size-space-4);
flex-wrap: wrap;
align-content: stretch;
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/workspace/workspace.context.ts b/uSync.Backoffice.Management.Client/usync-assets/src/workspace/workspace.context.ts
index f3cb9253a..781144817 100644
--- a/uSync.Backoffice.Management.Client/usync-assets/src/workspace/workspace.context.ts
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/workspace/workspace.context.ts
@@ -47,6 +47,7 @@ export class uSyncWorkspaceContext
*/
#actions = new UmbArrayState([], (x) => x.key);
public readonly actions = this.#actions.asObservable();
+ private _currentSetName: string = '';
/**
* the working group name
@@ -134,6 +135,7 @@ export class uSyncWorkspaceContext
if (data) {
this.#actions.setValue(data);
+ this._currentSetName = setName;
}
}
@@ -250,6 +252,7 @@ export class uSyncWorkspaceContext
if (complete) {
this.#results.setValue(data?.actions ?? []);
+ this.getActions(this._currentSetName);
}
} else {
complete = true;
diff --git a/uSync.Core/ChangeType.cs b/uSync.Core/ChangeType.cs
index c3670e6c0..0af6c3acf 100644
--- a/uSync.Core/ChangeType.cs
+++ b/uSync.Core/ChangeType.cs
@@ -1,10 +1,12 @@
using System.Runtime.Serialization;
+using System.Text.Json.Serialization;
namespace uSync.Core;
///
/// Type of change performed
///
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ChangeType : int
{
[EnumMember(Value = "Clean")]
diff --git a/uSync.Core/Dependency/uSyncDependency.cs b/uSync.Core/Dependency/uSyncDependency.cs
index 551606015..9123d9194 100644
--- a/uSync.Core/Dependency/uSyncDependency.cs
+++ b/uSync.Core/Dependency/uSyncDependency.cs
@@ -1,4 +1,6 @@
-using Umbraco.Cms.Core;
+using System.Text.Json.Serialization;
+
+using Umbraco.Cms.Core;
namespace uSync.Core.Dependency;
@@ -90,6 +92,7 @@ private static void FireUpdate(string message, int count, int total)
///
/// the match mode for the dependency (reserved)
///
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum DependencyMode
{
MustMatch,
diff --git a/uSync.Core/Models/uSyncChange.cs b/uSync.Core/Models/uSyncChange.cs
index 2a19f9e5d..4277171e7 100644
--- a/uSync.Core/Models/uSyncChange.cs
+++ b/uSync.Core/Models/uSyncChange.cs
@@ -100,6 +100,7 @@ public static uSyncChange Warning(string path, string name, string warning)
};
}
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ChangeDetailType
{
NoChange,
diff --git a/uSync.Core/Sync/SyncTreeType.cs b/uSync.Core/Sync/SyncTreeType.cs
index 0349d56ec..86f5538ef 100644
--- a/uSync.Core/Sync/SyncTreeType.cs
+++ b/uSync.Core/Sync/SyncTreeType.cs
@@ -1,4 +1,6 @@
-namespace uSync.Core.Sync;
+using System.Text.Json.Serialization;
+
+namespace uSync.Core.Sync;
///
/// The type of tree item this is.
@@ -15,6 +17,7 @@
/// we can treat it as a 'settings' tree and show the
/// menus when they are valid.
///
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum SyncTreeType
{
///
diff --git a/uSync.Core/SyncActionType.cs b/uSync.Core/SyncActionType.cs
index 81530a177..d4e70e271 100644
--- a/uSync.Core/SyncActionType.cs
+++ b/uSync.Core/SyncActionType.cs
@@ -1,9 +1,12 @@
-namespace uSync.Core;
+using System.Text.Json.Serialization;
+
+namespace uSync.Core;
///
/// indicates what happened to an item to cause a ghost file
/// to be present.
///
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum SyncActionType
{
None = 0,
diff --git a/uSync.Core/Tracking/SyncXmlTracker.cs b/uSync.Core/Tracking/SyncXmlTracker.cs
index 43a826a15..ba1bdb9c8 100644
--- a/uSync.Core/Tracking/SyncXmlTracker.cs
+++ b/uSync.Core/Tracking/SyncXmlTracker.cs
@@ -1,4 +1,5 @@
using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
using System.Xml.Linq;
using System.Xml.XPath;
@@ -414,6 +415,7 @@ public class TrackingKey
public bool IsAttribute { get; set; }
}
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum TrackingDirection
{
TargetToSource,
diff --git a/uSync.Core/uSyncContentState.cs b/uSync.Core/uSyncContentState.cs
index d38715f2d..94b2110e0 100644
--- a/uSync.Core/uSyncContentState.cs
+++ b/uSync.Core/uSyncContentState.cs
@@ -1,8 +1,11 @@
-namespace uSync.Core;
+using System.Text.Json.Serialization;
+
+namespace uSync.Core;
///
/// the state we thing a culture / content item should be in.
///
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum uSyncContentState
{
Saved, Unpublished, Published