diff --git a/Directory.Packages.props b/Directory.Packages.props
index 2fc28662..dd3d247e 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,36 +1,36 @@
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uSync.BackOffice/Extensions/uSyncActionExtensions.cs b/uSync.BackOffice/Extensions/uSyncActionExtensions.cs
index 923eafb1..e9cf8146 100644
--- a/uSync.BackOffice/Extensions/uSyncActionExtensions.cs
+++ b/uSync.BackOffice/Extensions/uSyncActionExtensions.cs
@@ -137,7 +137,9 @@ public static void UpdateActions(this List actions, Guid k
public static bool RequiresSave(this SyncAttempt attempt)
=> attempt.Success && attempt.Change > Core.ChangeType.NoChange && !attempt.Saved && attempt.Item != null;
-
+ ///
+ /// return the uSyncAction as an ActionView (used in the controllers)
+ ///
public static uSyncActionView AsActionView(this uSyncAction action)
{
var msg = string.IsNullOrWhiteSpace(action.Message) is false
diff --git a/uSync.BackOffice/Services/ISyncVersionFileService.cs b/uSync.BackOffice/Services/ISyncVersionFileService.cs
index b3355932..90152666 100644
--- a/uSync.BackOffice/Services/ISyncVersionFileService.cs
+++ b/uSync.BackOffice/Services/ISyncVersionFileService.cs
@@ -2,8 +2,20 @@
namespace uSync.BackOffice.Services;
+///
+/// Controls the version file we write to disk on syncs (used to warn if sync is old)
+///
public interface ISyncVersionFileService
{
+ ///
+ /// get the Sync file version information for a folder.
+ ///
Task GetSyncFileInfo(string folder);
+
+ ///
+ /// write the version information to disk.
+ ///
+ ///
+ ///
Task WriteVersionFileAsync(string folder);
}
\ No newline at end of file
diff --git a/uSync.BackOffice/Services/SyncVersionFileService.cs b/uSync.BackOffice/Services/SyncVersionFileService.cs
index af146b8e..fee5c640 100644
--- a/uSync.BackOffice/Services/SyncVersionFileService.cs
+++ b/uSync.BackOffice/Services/SyncVersionFileService.cs
@@ -119,9 +119,23 @@ private bool HmacValuesMatch(XElement node)
}
}
+///
+/// results of a check of the version file
+///
public class SyncFileVersionCheckResult
{
+ ///
+ /// the sync on disk is current to the current format we are writing.
+ ///
public bool IsCurrent { get; set; }
+
+ ///
+ /// the version we are writing to disk
+ ///
public string? FormatVersion { get; set; }
+
+ ///
+ /// the hmac value for the folders matches. (reserved)
+ ///
public bool HmacMatch { get; set; }
}
diff --git a/uSync.BackOffice/uSyncBackOffice.cs b/uSync.BackOffice/uSyncBackOffice.cs
index 2ac79b1f..8f9f519e 100644
--- a/uSync.BackOffice/uSyncBackOffice.cs
+++ b/uSync.BackOffice/uSyncBackOffice.cs
@@ -7,6 +7,9 @@ namespace uSync.BackOffice;
///
public class uSync
{
+ ///
+ /// assembly version for uSync
+ ///
public static Version Version => typeof(uSync).Assembly.GetName().Version ?? new Version(15, 0, 0);
///
diff --git a/uSync.Backoffice.Management.Client/usync-assets/package-lock.json b/uSync.Backoffice.Management.Client/usync-assets/package-lock.json
index 6f9c935c..a0c082e7 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": "17.3.2",
+ "version": "17.3.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@jumoo/usync",
- "version": "17.3.2",
+ "version": "17.3.3",
"license": "MPL-2.0",
"devDependencies": {
"@hey-api/openapi-ts": "^0.95.0",
diff --git a/uSync.Backoffice.Management.Client/usync-assets/package.json b/uSync.Backoffice.Management.Client/usync-assets/package.json
index 9ac73a50..82b5cbd2 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": "17.3.2",
+ "version": "17.3.3",
"main": "./dist/usync.js",
"types": "./dist/index.d.ts",
"module": "./dist/usync.js",
diff --git a/uSync.Core/Mapping/Mappers/ImagePathMapper.cs b/uSync.Core/Mapping/Mappers/ImagePathMapper.cs
index 807fd9c7..a8730d28 100644
--- a/uSync.Core/Mapping/Mappers/ImagePathMapper.cs
+++ b/uSync.Core/Mapping/Mappers/ImagePathMapper.cs
@@ -2,8 +2,6 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
-using System.Text.RegularExpressions;
-
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Media;
@@ -11,7 +9,6 @@
using Umbraco.Cms.Core.Services;
using Umbraco.Extensions;
-using uSync.Core.Dependency;
using uSync.Core.Extensions;
using uSync.Core.Serialization;
@@ -28,49 +25,33 @@ namespace uSync.Core.Mapping;
/// becomes
/// {"src":"/media/2cud1lzo/15656993711_ccd199b83e_k.jpg","crops":null}
///
-public class ImagePathMapper : SyncValueMapperBase, ISyncMapper
+public class ImagePathMapper : ImagePathMapperBase, ISyncMapper
{
- private const string _genericMediaPath = "/media";
-
- private readonly string _siteRoot;
- private string? _mediaFolder;
- private readonly ILogger _logger;
- private readonly IConfiguration _configuration;
private readonly IImageUrlGenerator _imageUrlGenerator;
public ImagePathMapper(
- IConfiguration configuration,
- IOptionsMonitor _globalOptions,
IEntityService entityService,
ILogger logger,
- IImageUrlGenerator imageUrlGenerator) : base(entityService)
+ IConfiguration configuration,
+ IOptionsMonitor globalOptions,
+ IImageUrlGenerator imageUrlGenerator) : base(entityService, logger, configuration, globalOptions)
{
- _logger = logger;
- _configuration = configuration;
-
- // todo: site root might need us to include extra NuGet.
- _siteRoot = "";
-
- _mediaFolder = GetMediaFolderSetting(_globalOptions.CurrentValue.UmbracoMediaPath.TrimStart('~'));
- _globalOptions.OnChange(x => _mediaFolder = GetMediaFolderSetting(x.UmbracoMediaPath.TrimStart('~')));
-
- if (logger.IsEnabled(LogLevel.Debug))
- logger.LogDebug("Media Folders: [{media}]", _mediaFolder ?? "(Blank)");
-
_imageUrlGenerator = imageUrlGenerator;
}
public override string Name => "ImageCropper Mapper";
public override string[] Editors => [
- Constants.PropertyEditors.Aliases.ImageCropper,
- Constants.PropertyEditors.Aliases.UploadField
+ Constants.PropertyEditors.Aliases.ImageCropper
];
public override Task GetExportValueAsync(object value, string editorAlias)
{
return uSyncTaskHelper.FromResultOf(() =>
{
+ if (_logger.IsEnabled(LogLevel.Debug))
+ _logger.LogDebug("Getting export value for ImageCropper with value {Value}", value);
+
var stringValue = value?.ToString();
if (string.IsNullOrWhiteSpace(stringValue)) return stringValue;
@@ -107,76 +88,6 @@ public ImagePathMapper(
});
}
- private string StripSitePath(string filePath)
- {
- var path = filePath;
- if (_siteRoot.Length > 0 && !string.IsNullOrWhiteSpace(filePath) && filePath.InvariantStartsWith(_siteRoot))
- path = filePath.Substring(_siteRoot.Length);
-
- return ReplacePath(path, _mediaFolder, _genericMediaPath);
- }
-
- private string PrePendSitePath(string filePath)
- {
- var path = filePath;
- if (_siteRoot.Length > 0 && !string.IsNullOrEmpty(filePath))
- path = $"{_siteRoot}{filePath}";
-
- return ReplacePath(path, _genericMediaPath, _mediaFolder);
- }
-
-
- ///
- /// makes a specific media path generic.
- ///
- ///
- /// sometimes paths may be defined by umbraco settings, (especially blob settings)
- /// that mean they are not stored as /media
- ///
- /// for the sake of generic importing we want the folder stored to be /media.
- /// so we re-write the setting on import and export
- ///
- /// assumes you have a app setting in the web.config
- ///
- /// /someFolder
- ///
- ///
- ///
- private static string ReplacePath(string filePath, string? currentPath, string? targetPath)
- {
- if (!string.IsNullOrWhiteSpace(targetPath)
- && !string.IsNullOrWhiteSpace(currentPath)
- && !currentPath.Equals(targetPath))
- {
- return Regex.Replace(filePath, $"^{currentPath}", targetPath, RegexOptions.IgnoreCase);
- }
-
- return filePath;
- }
-
- ///
- /// Get the media rewrite folder
- ///
- ///
- /// looks in appSettings for uSync:mediaFolder
- ///
- ///
- ///
- /// or in uSync8.config for media setting
- ///
- ///
- ///
- /// /someFolder
- ///
- ///
- ///
- private string GetMediaFolderSetting(string umbracoMediaPath)
- {
- var folder = this._configuration.GetValue("uSync:MediaFolder", string.Empty);
- if (!string.IsNullOrEmpty(folder)) return folder;
-
- return umbracoMediaPath;
- }
public override Task GetImportValueAsync(string value, string editorAlias, SyncSerializerOptions options)
{
@@ -202,49 +113,4 @@ private string GetMediaFolderSetting(string umbracoMediaPath)
return json.SerializeJsonNode(true);
});
}
-
- ///
- /// Get the actual media file as a dependency.
- ///
- public override Task> GetDependenciesAsync(object value, string editorAlias, DependencyFlags flags)
- {
- return uSyncTaskHelper.FromResultOf>(() =>
- {
-
- var stringValue = value?.ToString();
- if (string.IsNullOrWhiteSpace(stringValue))
- return [];
-
- var stringPath = GetImagePath(stringValue).TrimStart('/').ToLower();
-
- if (!string.IsNullOrWhiteSpace(stringPath))
- {
- return [new uSyncDependency()
- {
- Name = $"File: {Path.GetFileName(stringPath)}",
- Udi = Udi.Create(Constants.UdiEntityType.MediaFile, stringPath),
- Flags = flags,
- Order = DependencyOrders.OrderFromEntityType(Constants.UdiEntityType.MediaFile),
- Level = 0
- }];
- }
-
- return [];
- });
- }
-
- private string GetImagePath(string stringValue)
- {
- if (stringValue.TryParseToJsonObject(out var json) is false || json is null)
- return StripSitePath(stringValue);
-
-
- if (json.TryGetPropertyValue("src", out var srcNode) is true)
- {
- var source = srcNode?.GetValue() ?? string.Empty;
- if (string.IsNullOrWhiteSpace(source) is false) return source;
- }
-
- return string.Empty;
- }
}
diff --git a/uSync.Core/Mapping/Mappers/ImagePathMapperBase.cs b/uSync.Core/Mapping/Mappers/ImagePathMapperBase.cs
new file mode 100644
index 00000000..b60b5579
--- /dev/null
+++ b/uSync.Core/Mapping/Mappers/ImagePathMapperBase.cs
@@ -0,0 +1,163 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+using System.Text.RegularExpressions;
+
+using Umbraco.Cms.Core;
+using Umbraco.Cms.Core.Configuration.Models;
+using Umbraco.Cms.Core.Services;
+using Umbraco.Extensions;
+
+using uSync.Core.Dependency;
+using uSync.Core.Extensions;
+
+namespace uSync.Core.Mapping;
+
+public abstract class ImagePathMapperBase : SyncValueMapperBase
+{
+ private readonly IConfiguration _configuration;
+ protected readonly ILogger _logger;
+
+ private const string _genericMediaPath = "/media";
+ private readonly string _siteRoot;
+ private string? _mediaFolder;
+
+ public ImagePathMapperBase(
+ IEntityService entityService,
+ ILogger logger,
+ IConfiguration configuration,
+ IOptionsMonitor globalOptions
+ ) : base(entityService)
+ {
+ _configuration = configuration;
+ _logger = logger;
+
+ // todo: site root might need us to include extra NuGet.
+ _siteRoot = "";
+
+ _mediaFolder = GetMediaFolderSetting(globalOptions.CurrentValue.UmbracoMediaPath.TrimStart('~'));
+ globalOptions.OnChange(x => _mediaFolder = GetMediaFolderSetting(x.UmbracoMediaPath.TrimStart('~')));
+
+ if (logger.IsEnabled(LogLevel.Debug))
+ logger.LogDebug("Media Folders: [{media}]", _mediaFolder ?? "(Blank)");
+
+ }
+
+
+ protected string StripSitePath(string filePath)
+ {
+ var path = filePath;
+ if (_siteRoot.Length > 0 && !string.IsNullOrWhiteSpace(filePath) && filePath.InvariantStartsWith(_siteRoot))
+ path = filePath.Substring(_siteRoot.Length);
+
+ return ReplacePath(path, _mediaFolder, _genericMediaPath);
+ }
+
+ protected string PrePendSitePath(string filePath)
+ {
+ var path = filePath;
+ if (_siteRoot.Length > 0 && !string.IsNullOrEmpty(filePath))
+ path = $"{_siteRoot}{filePath}";
+
+ return ReplacePath(path, _genericMediaPath, _mediaFolder);
+ }
+
+
+ ///
+ /// makes a specific media path generic.
+ ///
+ ///
+ /// sometimes paths may be defined by umbraco settings, (especially blob settings)
+ /// that mean they are not stored as /media
+ ///
+ /// for the sake of generic importing we want the folder stored to be /media.
+ /// so we re-write the setting on import and export
+ ///
+ /// assumes you have a app setting in the web.config
+ ///
+ /// /someFolder
+ ///
+ ///
+ ///
+ private static string ReplacePath(string filePath, string? currentPath, string? targetPath)
+ {
+ if (!string.IsNullOrWhiteSpace(targetPath)
+ && !string.IsNullOrWhiteSpace(currentPath)
+ && !currentPath.Equals(targetPath))
+ {
+ return Regex.Replace(filePath, $"^{currentPath}", targetPath, RegexOptions.IgnoreCase);
+ }
+
+ return filePath;
+ }
+
+ ///
+ /// Get the media rewrite folder
+ ///
+ ///
+ /// looks in appSettings for uSync:mediaFolder
+ ///
+ ///
+ ///
+ /// or in uSync8.config for media setting
+ ///
+ ///
+ ///
+ /// /someFolder
+ ///
+ ///
+ ///
+ private string GetMediaFolderSetting(string umbracoMediaPath)
+ {
+ var folder = this._configuration.GetValue("uSync:MediaFolder", string.Empty);
+ if (!string.IsNullOrEmpty(folder)) return folder;
+
+ return umbracoMediaPath;
+ }
+
+ ///
+ /// Get the actual media file as a dependency.
+ ///
+ public override Task> GetDependenciesAsync(object value, string editorAlias, DependencyFlags flags)
+ {
+ return uSyncTaskHelper.FromResultOf>(() =>
+ {
+
+ var stringValue = value?.ToString();
+ if (string.IsNullOrWhiteSpace(stringValue))
+ return [];
+
+ var stringPath = GetImagePath(stringValue).TrimStart('/').ToLower();
+
+ if (!string.IsNullOrWhiteSpace(stringPath))
+ {
+ return [new uSyncDependency()
+ {
+ Name = $"File: {Path.GetFileName(stringPath)}",
+ Udi = Udi.Create(Constants.UdiEntityType.MediaFile, stringPath),
+ Flags = flags,
+ Order = DependencyOrders.OrderFromEntityType(Constants.UdiEntityType.MediaFile),
+ Level = 0
+ }];
+ }
+
+ return [];
+ });
+ }
+
+ private string GetImagePath(string stringValue)
+ {
+ if (stringValue.TryParseToJsonObject(out var json) is false || json is null)
+ return StripSitePath(stringValue);
+
+
+ if (json.TryGetPropertyValue("src", out var srcNode) is true)
+ {
+ var source = srcNode?.GetValue() ?? string.Empty;
+ if (string.IsNullOrWhiteSpace(source) is false) return source;
+ }
+
+ return string.Empty;
+ }
+}
diff --git a/uSync.Core/Mapping/Mappers/ImageUploadMapper.cs b/uSync.Core/Mapping/Mappers/ImageUploadMapper.cs
new file mode 100644
index 00000000..bd47dbf4
--- /dev/null
+++ b/uSync.Core/Mapping/Mappers/ImageUploadMapper.cs
@@ -0,0 +1,51 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+using Umbraco.Cms.Core;
+using Umbraco.Cms.Core.Configuration.Models;
+using Umbraco.Cms.Core.Services;
+
+using uSync.Core.Extensions;
+using uSync.Core.Serialization;
+
+namespace uSync.Core.Mapping;
+
+///
+/// image uploads don't store any of the json, stuff, so they are similar to image croppers,
+/// but a bit simpler.
+///
+public class ImageUploadMapper : ImagePathMapperBase, ISyncMapper
+{
+ public ImageUploadMapper(
+ IEntityService entityService,
+ ILogger logger,
+ IConfiguration configuration,
+ IOptionsMonitor globalOptions) : base(entityService, logger, configuration, globalOptions)
+ { }
+
+ public override string Name => "Image Upload Mapper";
+ public override string[] Editors => [Constants.PropertyEditors.Aliases.UploadField];
+ public override Task GetExportValueAsync(object value, string editorAlias)
+ {
+ return uSyncTaskHelper.FromResultOf(() =>
+ {
+ if (_logger.IsEnabled(LogLevel.Debug))
+ _logger.LogDebug("Getting export value for ImageUpload with value {Value}", value);
+
+ var stringValue = value?.ToString();
+ if (string.IsNullOrWhiteSpace(stringValue)) return stringValue;
+ return StripSitePath(stringValue);
+ });
+ }
+
+ public override Task GetImportValueAsync(string value, string editorAlias, SyncSerializerOptions options)
+ {
+ return uSyncTaskHelper.FromResultOf(() =>
+ {
+ var stringValue = value?.ToString();
+ if (string.IsNullOrWhiteSpace(stringValue)) return stringValue;
+ return PrePendSitePath(stringValue);
+ });
+ }
+}
diff --git a/uSync.Core/Mapping/SyncBlockMapperBase.cs b/uSync.Core/Mapping/SyncBlockMapperBase.cs
index b9d24030..267d288e 100644
--- a/uSync.Core/Mapping/SyncBlockMapperBase.cs
+++ b/uSync.Core/Mapping/SyncBlockMapperBase.cs
@@ -69,7 +69,8 @@ public SyncBlockMapperBase(
// and prevent double-encoding when the block value is re-serialized.
if (result is string stringResult && value.IsNonStringJsonValue())
{
- return stringResult.ConvertToJsonNode() ?? result;
+ return stringResult.ConvertStringToExpandedJson() ?? result;
+ // return stringResult.ConvertToJsonNode() ?? result;
}
return result;
diff --git a/uSync.History/history-client/package-lock.json b/uSync.History/history-client/package-lock.json
index c9bd6758..426fb256 100644
--- a/uSync.History/history-client/package-lock.json
+++ b/uSync.History/history-client/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "usync-history-client",
- "version": "17.3.2",
+ "version": "17.3.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "usync-history-client",
- "version": "17.3.2",
+ "version": "17.3.3",
"devDependencies": {
"@hey-api/openapi-ts": "^0.95.0",
"@jumoo/translate": "^17.2.3",
diff --git a/uSync.History/history-client/package.json b/uSync.History/history-client/package.json
index bca2bcfc..9696c20e 100644
--- a/uSync.History/history-client/package.json
+++ b/uSync.History/history-client/package.json
@@ -1,6 +1,6 @@
{
"name": "usync-history-client",
- "version": "17.3.2",
+ "version": "17.3.3",
"licence": "Custom",
"description": "uSync history function",
"type": "module",
diff --git a/uSync.Tests/packages.lock.json b/uSync.Tests/packages.lock.json
index 2e15ba6c..8c9df6ff 100644
--- a/uSync.Tests/packages.lock.json
+++ b/uSync.Tests/packages.lock.json
@@ -2616,7 +2616,7 @@
},
"Umbraco.Cms": {
"type": "CentralTransitive",
- "requested": "[17.3.0, )",
+ "requested": "[17.4.2, )",
"resolved": "17.3.0",
"contentHash": "yaG/li5/5RT7354r6rloErE96HojsDEHyYE+iVxcb+ySCLn2PF424qVSXEYq5ZXAbN4Zbx2/6oytTA4HX5w2rg==",
"dependencies": {