Skip to content

Commit 59fecff

Browse files
authored
Revive uSync.Community.DataTypeSerializers with Try-pattern base and new serializers (#1052) (#1053)
Convert SyncDataTypeSerializerBase's guid/path lookups (UdiToEntityPath, GuidToEntityPath, PathToUdi, PathToGuid, FindItem) to a Try* pattern instead of relying on empty-string/null sentinels for failure. Add/update editor config serializers that map node references to portable entity paths on export and back to guids on import: - ContentPickerConfigSerializer (startNodeId) - MediaPicker3ConfigSerializer (startNodeId) - MNTPickerConfigSerializer (startNode.id, startNode.dynamicRoot.originKey, the latter only mapped when present) - RichTextConfigSerializer (mediaParentId) - new, defensively copies the configuration dictionary before mutating since other serializers for the same editor (e.g. uSync.Core's RichTextEditorMigratingSerializer) can hand back a read-only ImmutableSortedDictionary.
1 parent f09acdc commit 59fecff

5 files changed

Lines changed: 209 additions & 170 deletions

File tree

Lines changed: 20 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23

34
using Umbraco.Cms.Core.Services;
45

6+
using uSync.Community.DataTypeSerializers;
57
using uSync.Core.DataTypes;
68

7-
namespace uSync8.Community.DataTypeSerializers.CoreTypes;
9+
namespace uSync.Community.DataTypeSerializers.CoreTypes;
810

911
public class ContentPickerConfigSerializer : SyncDataTypeSerializerBase, IConfigurationSerializer
1012
{
@@ -13,57 +15,31 @@ public ContentPickerConfigSerializer(IEntityService entityService)
1315
{ }
1416

1517
public string Name => "ContentPickerNodeSerializer";
16-
1718
public string[] Editors => ["Umbraco.ContentPicker"];
1819

20+
private const string _startNodeIdKey = "startNodeId";
21+
1922
public override IDictionary<string, object> GetConfigurationExport(IDictionary<string, object> configuration)
2023
{
24+
if (configuration.TryGetValue(_startNodeIdKey, out var startNodeId)
25+
&& Guid.TryParse(startNodeId.ToString(), out var startNodeGuid)
26+
&& TryGuidToEntityPath(startNodeGuid, out var entityPath))
27+
{
28+
configuration["startNodeId"] = entityPath;
29+
}
30+
2131
return base.GetConfigurationExport(configuration);
2232
}
2333

2434
public override IDictionary<string, object> GetConfigurationImport(IDictionary<string, object> configuration)
2535
{
36+
if (configuration.TryGetValue(_startNodeIdKey, out var startNodeId)
37+
&& startNodeId is string startNodePath
38+
&& TryPathToGuid(startNodePath, out var startNodeGuid))
39+
{
40+
configuration["startNodeId"] = startNodeGuid;
41+
}
42+
2643
return base.GetConfigurationImport(configuration);
2744
}
28-
29-
//public override string? SerializeConfig(object configuration)
30-
//{
31-
32-
// if (configuration is ContentPickerConfiguration pickerConfig)
33-
// {
34-
// var contentPickerConfig = new MappedPathConfigBase<ContentPickerConfiguration>();
35-
36-
// contentPickerConfig.Config = new ContentPickerConfiguration()
37-
// {
38-
// IgnoreUserStartNodes = pickerConfig.IgnoreUserStartNodes,
39-
// //StartNodeId = null,
40-
// //ShowOpenButton = pickerConfig.ShowOpenButton
41-
// };
42-
43-
// //if (pickerConfig.StartNodeId != null)
44-
// // contentPickerConfig.MappedPath = UdiToEntityPath(pickerConfig.StartNodeId);
45-
46-
// return base.SerializeConfig(contentPickerConfig);
47-
// }
48-
49-
// return base.SerializeConfig(configuration);
50-
//}
51-
52-
53-
//public override object? DeserializeConfig(string config, Type configType)
54-
//{
55-
// if (configType == typeof(ContentPickerConfiguration))
56-
// {
57-
// var mappedConfig = config.DeserializeJson<MappedPathConfigBase<ContentPickerConfiguration>>();
58-
59-
// //if (!string.IsNullOrWhiteSpace(mappedConfig.MappedPath))
60-
// //{
61-
// // mappedConfig.Config.StartNodeId = PathToUdi(mappedConfig.MappedPath);
62-
// //}
63-
64-
// return mappedConfig?.Config;
65-
// }
66-
67-
// return base.DeserializeConfig(config, configType);
68-
//}
6945
}
Lines changed: 65 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text.Json.Nodes;
24

35
using Umbraco.Cms.Core.Services;
46

57
using uSync.Core.DataTypes;
8+
using uSync.Core.Extensions;
69

7-
namespace uSync8.Community.DataTypeSerializers.CoreTypes;
10+
namespace uSync.Community.DataTypeSerializers.CoreTypes;
811

912
public class MNTPickerConfigSerializer : SyncDataTypeSerializerBase, IConfigurationSerializer
1013
{
@@ -16,64 +19,74 @@ public MNTPickerConfigSerializer(IEntityService entityService)
1619

1720
public string[] Editors => ["Umbraco.MultiNodeTreePicker"];
1821

22+
private const string _startNodeKey = "startNode";
23+
private const string _idKey = "id";
24+
private const string _dynamicRootKey = "dynamicRoot";
25+
private const string _originKeyKey = "originKey";
26+
1927
public override IDictionary<string, object> GetConfigurationExport(IDictionary<string, object> configuration)
2028
{
29+
if (configuration.TryGetValue(_startNodeKey, out var startNodeValue)
30+
&& startNodeValue is not null
31+
&& startNodeValue.TryConvertToJsonObject(out var startNode))
32+
{
33+
TryMapNodeValue(startNode, _idKey, TryGuidToEntityPath);
34+
35+
if (startNode.TryGetPropertyAsObject(_dynamicRootKey, out var dynamicRoot))
36+
TryMapNodeValue(dynamicRoot, _originKeyKey, TryGuidToEntityPath);
37+
38+
configuration[_startNodeKey] = startNode;
39+
}
40+
2141
return base.GetConfigurationExport(configuration);
2242
}
2343

2444
public override IDictionary<string, object> GetConfigurationImport(IDictionary<string, object> configuration)
2545
{
46+
if (configuration.TryGetValue(_startNodeKey, out var startNodeValue)
47+
&& startNodeValue is not null
48+
&& startNodeValue.TryConvertToJsonObject(out var startNode))
49+
{
50+
TryMapNodeValue(startNode, _idKey, TryPathToGuid);
51+
52+
if (startNode.TryGetPropertyAsObject(_dynamicRootKey, out var dynamicRoot))
53+
TryMapNodeValue(dynamicRoot, _originKeyKey, TryPathToGuid);
54+
55+
configuration[_startNodeKey] = startNode;
56+
}
57+
2658
return base.GetConfigurationImport(configuration);
2759
}
2860

29-
//public override string SerializeConfig(object configuration)
30-
//{
31-
// var MNTPMappedConfig = new MappedPathConfigBase<MultiNodePickerConfiguration>();
32-
33-
// if (configuration is MultiNodePickerConfiguration pickerConfig)
34-
// {
35-
// MNTPMappedConfig.Config = new MultiNodePickerConfiguration()
36-
// {
37-
// IgnoreUserStartNodes = pickerConfig.IgnoreUserStartNodes,
38-
// // Filter = pickerConfig.Filter,
39-
// MaxNumber = pickerConfig.MaxNumber,
40-
// MinNumber = pickerConfig.MinNumber,
41-
// // ShowOpen = pickerConfig.ShowOpen,
42-
// TreeSource = new MultiNodePickerConfigurationTreeSource()
43-
// {
44-
// ObjectType = pickerConfig.TreeSource.ObjectType,
45-
// StartNodeId = pickerConfig.TreeSource.StartNodeId,
46-
// StartNodeQuery = pickerConfig.TreeSource.StartNodeQuery
47-
// }
48-
49-
// };
50-
51-
// if (pickerConfig?.TreeSource?.StartNodeId != null)
52-
// {
53-
// MNTPMappedConfig.MappedPath = UdiToEntityPath(pickerConfig.TreeSource.StartNodeId);
54-
// }
55-
56-
// return base.SerializeConfig(MNTPMappedConfig);
57-
// }
58-
59-
// return base.SerializeConfig(configuration);
60-
//}
61-
62-
63-
//public override object DeserializeConfig(string config, Type configType)
64-
//{
65-
// if (configType == typeof(MultiNodePickerConfiguration))
66-
// {
67-
// var mappedConfig = config.DeserializeJson<MappedPathConfigBase<MultiNodePickerConfiguration>>();
68-
69-
// if (!string.IsNullOrWhiteSpace(mappedConfig.MappedPath))
70-
// {
71-
// mappedConfig.Config.TreeSource.StartNodeId = PathToUdi(mappedConfig.MappedPath);
72-
// }
73-
74-
// return mappedConfig.Config;
75-
// }
76-
77-
// return base.DeserializeConfig(config, configType);
78-
//}
61+
private delegate bool TryConvert<TResult>(Guid guid, out TResult result);
62+
private delegate bool TryConvertBack(string value, out Guid guid);
63+
64+
/// <summary>
65+
/// maps a guid property value to its entity path, if the property is present and holds a guid.
66+
/// </summary>
67+
private static bool TryMapNodeValue(JsonObject node, string propertyName, TryConvert<string> converter)
68+
{
69+
if (node.TryGetPropertyValue(propertyName, out var propertyValue) is false
70+
|| propertyValue is null) return false;
71+
72+
if (Guid.TryParse(propertyValue.ToString(), out var guid) is false) return false;
73+
if (converter(guid, out var path) is false) return false;
74+
75+
node[propertyName] = path;
76+
return true;
77+
}
78+
79+
/// <summary>
80+
/// maps an entity path property value back to a guid, if the property is present and holds a path.
81+
/// </summary>
82+
private static bool TryMapNodeValue(JsonObject node, string propertyName, TryConvertBack converter)
83+
{
84+
if (node.TryGetPropertyValue(propertyName, out var propertyValue) is false
85+
|| propertyValue is null) return false;
86+
87+
if (converter(propertyValue.ToString(), out var guid) is false) return false;
88+
89+
node[propertyName] = guid;
90+
return true;
91+
}
7992
}
Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23

34
using Umbraco.Cms.Core.Services;
45

6+
using uSync.Community.DataTypeSerializers;
57
using uSync.Core.DataTypes;
68

7-
namespace uSync8.Community.DataTypeSerializers.CoreTypes;
9+
namespace uSync.Community.DataTypeSerializers.CoreTypes;
810

911
public class MediaPicker3ConfigSerializer : SyncDataTypeSerializerBase, IConfigurationSerializer
1012
{
@@ -16,56 +18,29 @@ public MediaPicker3ConfigSerializer(IEntityService entityService)
1618

1719
public string[] Editors => ["Umbraco.MediaPicker3"];
1820

19-
public override IDictionary<string, object> GetConfigurationImport(IDictionary<string, object> configuration)
20-
{
21-
return base.GetConfigurationImport(configuration);
22-
}
21+
private const string _startNodeIdKey = "startNodeId";
2322

2423
public override IDictionary<string, object> GetConfigurationExport(IDictionary<string, object> configuration)
2524
{
25+
if (configuration.TryGetValue(_startNodeIdKey, out var startNodeId)
26+
&& Guid.TryParse(startNodeId?.ToString(), out var startNodeGuid)
27+
&& TryGuidToEntityPath(startNodeGuid, out var entityPath))
28+
{
29+
configuration[_startNodeIdKey] = entityPath;
30+
}
31+
2632
return base.GetConfigurationExport(configuration);
2733
}
2834

29-
// public override string SerializeConfig(object configuration)
30-
// {
31-
32-
// if (configuration is MediaPicker3Configuration pickerConfig)
33-
// {
34-
// var mediaPickerConfig = new MappedPathConfigBase<MediaPicker3Configuration>();
35-
// mediaPickerConfig.Config = new MediaPicker3Configuration()
36-
// {
37-
// EnableLocalFocalPoint = pickerConfig.EnableLocalFocalPoint,
38-
// Crops = pickerConfig.Crops,
39-
// Filter = pickerConfig.Filter,
40-
// IgnoreUserStartNodes = pickerConfig.IgnoreUserStartNodes,
41-
// Multiple = pickerConfig.Multiple,
42-
// ValidationLimit = pickerConfig.ValidationLimit
43-
// };
44-
45-
// if (pickerConfig.StartNodeId != null)
46-
// mediaPickerConfig.MappedPath = UdiToEntityPath(pickerConfig.StartNodeId);
47-
// return base.SerializeConfig(mediaPickerConfig);
48-
// }
49-
50-
// return base.SerializeConfig(configuration);
51-
52-
// }
53-
54-
55-
// public override object DeserializeConfig(string config, Type configType)
56-
// {
57-
// if (configType == typeof(MediaPicker3Configuration))
58-
// {
59-
// var mappedConfig = config.DeserializeJson<MappedPathConfigBase<MediaPicker3Configuration>>();
60-
61-
// if (!string.IsNullOrWhiteSpace(mappedConfig.MappedPath))
62-
// {
63-
// mappedConfig.Config.StartNodeId = PathToUdi(mappedConfig.MappedPath);
64-
// }
65-
66-
// return mappedConfig.Config;
67-
// }
35+
public override IDictionary<string, object> GetConfigurationImport(IDictionary<string, object> configuration)
36+
{
37+
if (configuration.TryGetValue(_startNodeIdKey, out var startNodeId)
38+
&& startNodeId is string startNodePath
39+
&& TryPathToGuid(startNodePath, out var startNodeGuid))
40+
{
41+
configuration[_startNodeIdKey] = startNodeGuid;
42+
}
6843

69-
// return base.DeserializeConfig(config, configType);
70-
// }
44+
return base.GetConfigurationImport(configuration);
45+
}
7146
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
using Umbraco.Cms.Core.Services;
5+
6+
using uSync.Community.DataTypeSerializers;
7+
using uSync.Core.DataTypes;
8+
9+
namespace uSync.Community.DataTypeSerializers.CoreTypes;
10+
11+
public class RichTextConfigSerializer : SyncDataTypeSerializerBase, IConfigurationSerializer
12+
{
13+
public RichTextConfigSerializer(IEntityService entityService)
14+
: base(entityService)
15+
{ }
16+
17+
public string Name => "RichTextNodeSerializer";
18+
19+
public string[] Editors => ["Umbraco.RichText"];
20+
21+
private const string _mediaParentIdKey = "mediaParentId";
22+
23+
public override IDictionary<string, object> GetConfigurationExport(IDictionary<string, object> configuration)
24+
{
25+
if (configuration.TryGetValue(_mediaParentIdKey, out var mediaParentId)
26+
&& Guid.TryParse(mediaParentId?.ToString(), out var mediaParentGuid)
27+
&& TryGuidToEntityPath(mediaParentGuid, out var entityPath))
28+
{
29+
// other serializers for this editor (e.g. uSync.Core's RichTextEditorMigratingSerializer)
30+
// may hand us a read-only dictionary, so copy before mutating.
31+
configuration = new Dictionary<string, object>(configuration)
32+
{
33+
[_mediaParentIdKey] = entityPath
34+
};
35+
}
36+
37+
return base.GetConfigurationExport(configuration);
38+
}
39+
40+
public override IDictionary<string, object> GetConfigurationImport(IDictionary<string, object> configuration)
41+
{
42+
if (configuration.TryGetValue(_mediaParentIdKey, out var mediaParentId)
43+
&& mediaParentId is string mediaParentPath
44+
&& TryPathToGuid(mediaParentPath, out var mediaParentGuid))
45+
{
46+
// other serializers for this editor (e.g. uSync.Core's RichTextEditorMigratingSerializer)
47+
// may hand us a read-only dictionary, so copy before mutating.
48+
configuration = new Dictionary<string, object>(configuration)
49+
{
50+
[_mediaParentIdKey] = mediaParentGuid
51+
};
52+
}
53+
54+
return base.GetConfigurationImport(configuration);
55+
}
56+
}

0 commit comments

Comments
 (0)