Skip to content

Commit 065a032

Browse files
Merge pull request #68 from umbraco/v14/bugfix/legacy-artifact-migrators
v14: Fix and enable remaining legacy artifact migrators
2 parents 2416b7e + dc2a14d commit 065a032

19 files changed

+397
-100
lines changed

src/Umbraco.Deploy.Contrib/Extensions/ArtifactMigratorCollectionBuilderExtensions.cs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,22 @@ public static ArtifactMigratorCollectionBuilder AddLegacyMigrators(this Artifact
2121
// Allowed at root and child content types to permissions
2222
.Append<ContentTypeArtifactJsonMigrator>()
2323
// Data types
24-
//.Append<CheckBoxListDataTypeArtifactMigrator>()
25-
//.Append<ColorPickerAliasDataTypeArtifactMigrator>()
24+
.Append<CheckBoxListDataTypeArtifactMigrator>()
25+
.Append<ColorPickerAliasDataTypeArtifactMigrator>()
2626
.Append<ContentPicker2DataTypeArtifactMigrator>()
2727
.Append<ContentPickerAliasDataTypeArtifactMigrator>()
2828
.Append<DateDataTypeArtifactMigrator>()
29-
//.Append<DropDownDataTypeArtifactMigrator>()
30-
//.Append<DropDownFlexibleDataTypeArtifactMigrator>()
31-
//.Append<DropdownlistMultiplePublishKeysDataTypeArtifactMigrator>()
32-
//.Append<DropdownlistPublishingKeysDataTypeArtifactMigrator>()
33-
//.Append<DropDownMultipleDataTypeArtifactMigrator>()
34-
//.Append<MediaPicker2DataTypeArtifactMigrator>()
35-
//.Append<MediaPickerDataTypeArtifactMigrator>()
29+
.Append<DropDownDataTypeArtifactMigrator>()
30+
.Append<DropDownFlexibleDataTypeArtifactMigrator>()
31+
.Append<DropdownlistMultiplePublishKeysDataTypeArtifactMigrator>()
32+
.Append<DropdownlistPublishingKeysDataTypeArtifactMigrator>()
33+
.Append<DropDownMultipleDataTypeArtifactMigrator>()
34+
.Append<MediaPicker2DataTypeArtifactMigrator>()
3635
.Append<MemberPicker2DataTypeArtifactMigrator>()
3736
.Append<MultiNodeTreePicker2DataTypeArtifactMigrator>()
38-
.Append<MultiNodeTreePickerDataTypeArtifactMigrator>()
39-
//.Append<MultipleMediaPickerDataTypeArtifactMigrator>()
37+
.Append<MultipleMediaPickerDataTypeArtifactMigrator>()
4038
.Append<NoEditDataTypeArtifactMigrator>()
41-
//.Append<RadioButtonListDataTypeArtifactMigrator>()
39+
.Append<RadioButtonListDataTypeArtifactMigrator>()
4240
.Append<RelatedLinks2DataTypeArtifactMigrator>()
4341
.Append<RelatedLinksDataTypeArtifactMigrator>()
4442
.Append<TextboxDataTypeArtifactMigrator>()

src/Umbraco.Deploy.Contrib/Migrators/Legacy/Content/DocumentArtifactJsonMigrator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public override JsonNode Migrate(JsonNode artifactJson)
2626
{
2727
schedule.Add(new JsonObject()
2828
{
29-
["Date"] = releaseDate,
29+
["Date"] = releaseDate.DeepClone(),
3030
["Culture"] = string.Empty,
3131
["Action"] = nameof(ContentScheduleAction.Release)
3232
});
@@ -36,7 +36,7 @@ public override JsonNode Migrate(JsonNode artifactJson)
3636
{
3737
schedule.Add(new JsonObject()
3838
{
39-
["Date"] = expireDate,
39+
["Date"] = expireDate.DeepClone(),
4040
["Culture"] = string.Empty,
4141
["Action"] = nameof(ContentScheduleAction.Expire)
4242
});

src/Umbraco.Deploy.Contrib/Migrators/Legacy/ContentType/ContentTypeArtifactJsonMigrator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ public override JsonNode Migrate(JsonNode artifactJson)
2626
{
2727
artifactJson["Permissions"] = new JsonObject()
2828
{
29-
["AllowedAtRoot"] = artifactJson["AllowedAtRoot"],
30-
["AllowedChildContentTypes"] = artifactJson["AllowedChildContentTypes"],
29+
["AllowedAtRoot"] = artifactJson["AllowedAtRoot"]?.DeepClone(),
30+
["AllowedChildContentTypes"] = artifactJson["AllowedChildContentTypes"]?.DeepClone(),
3131
};
3232

3333
return artifactJson;

src/Umbraco.Deploy.Contrib/Migrators/Legacy/DataType/ColorPickerAliasDataTypeArtifactMigrator.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using System.Linq;
23
using Umbraco.Cms.Core;
34
using Umbraco.Cms.Core.PropertyEditors;
45
using Umbraco.Cms.Core.Semver;
@@ -39,10 +40,16 @@ public ColorPickerAliasDataTypeArtifactMigrator(PropertyEditorCollection propert
3940
}
4041
else if (int.TryParse(key, out _) && value is string itemValue)
4142
{
43+
var hex = itemValue.ToLowerInvariant();
44+
if (hex.Length is 3)
45+
{
46+
hex = string.Join(string.Empty, hex.Select(c => $"{c}{c}"));
47+
}
48+
4249
toConfiguration.Items.Add(new ColorPickerConfiguration.ColorPickerItem()
4350
{
44-
Label = itemValue,
45-
Value = itemValue,
51+
Label = hex,
52+
Value = hex,
4653
});
4754
}
4855
}

src/Umbraco.Deploy.Contrib/Migrators/Legacy/DataType/ContentPickerReplaceDataTypeArtifactMigratorBase.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
using Umbraco.Cms.Core.Serialization;
66
using Umbraco.Deploy.Core;
77
using Umbraco.Deploy.Infrastructure.Artifacts;
8-
using Umbraco.Deploy.Infrastructure.Migrators;
98

109
namespace Umbraco.Deploy.Contrib.Migrators.Legacy;
1110

1211
/// <summary>
1312
/// Migrates the <see cref="DataTypeArtifact" /> to replace the editor alias with <see cref="Constants.PropertyEditors.Aliases.ContentPicker" /> and update the configuration.
1413
/// </summary>
15-
public abstract class ContentPickerReplaceDataTypeArtifactMigratorBase : ReplaceDataTypeArtifactMigratorBase
14+
public abstract class ContentPickerReplaceDataTypeArtifactMigratorBase : LegacyReplaceDataTypeArtifactMigratorBase
1615
{
1716
/// <summary>
1817
/// Initializes a new instance of the <see cref="ContentPickerReplaceDataTypeArtifactMigratorBase" /> class.
@@ -27,12 +26,9 @@ protected ContentPickerReplaceDataTypeArtifactMigratorBase(string fromEditorAlia
2726
/// <inheritdoc />
2827
protected override IDictionary<string, object>? MigrateConfiguration(IDictionary<string, object> configuration)
2928
{
30-
if (configuration.TryGetValue("startNodeId", out var startNodeIdValue) &&
31-
(startNodeIdValue?.ToString() is not string startNodeIdString || !UdiParser.TryParse(startNodeIdString, out _)))
32-
{
33-
// Remove invalid start node id
34-
configuration.Remove("startNodeId");
35-
}
29+
ReplaceIntegerWithBoolean(ref configuration, Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes);
30+
ReplaceUdiWithGuid(ref configuration, "startNodeId");
31+
ReplaceIntegerWithBoolean(ref configuration, "showOpenButton");
3632

3733
return configuration;
3834
}

src/Umbraco.Deploy.Contrib/Migrators/Legacy/DataType/DateDataTypeArtifactMigrator.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
using Umbraco.Cms.Core.Serialization;
66
using Umbraco.Deploy.Core;
77
using Umbraco.Deploy.Infrastructure.Artifacts;
8-
using Umbraco.Deploy.Infrastructure.Migrators;
98

109
namespace Umbraco.Deploy.Contrib.Migrators.Legacy;
1110

1211
/// <summary>
1312
/// Migrates the <see cref="DataTypeArtifact" /> to replace the <see cref="FromEditorAlias" /> editor with <see cref="Constants.PropertyEditors.Aliases.DateTime" />.
1413
/// </summary>
15-
public class DateDataTypeArtifactMigrator : ReplaceDataTypeArtifactMigratorBase
14+
public class DateDataTypeArtifactMigrator : LegacyReplaceDataTypeArtifactMigratorBase
1615
{
1716
private const string FromEditorAlias = "Umbraco.Date";
1817

@@ -27,5 +26,10 @@ public DateDataTypeArtifactMigrator(PropertyEditorCollection propertyEditors, IC
2726

2827
/// <inheritdoc />
2928
protected override IDictionary<string, object>? MigrateConfiguration(IDictionary<string, object> configuration)
30-
=> configuration;
29+
{
30+
ReplaceIntegerWithBoolean(ref configuration, "offsetTime");
31+
ReplaceIntegerWithBoolean(ref configuration, "defaultEmpty");
32+
33+
return configuration;
34+
}
3135
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics.CodeAnalysis;
4+
using System.Linq;
5+
using Umbraco.Cms.Core;
6+
using Umbraco.Cms.Core.PropertyEditors;
7+
using Umbraco.Cms.Core.Serialization;
8+
using Umbraco.Deploy.Infrastructure.Migrators;
9+
using Umbraco.Extensions;
10+
11+
namespace Umbraco.Deploy.Contrib.Migrators.Legacy;
12+
13+
/// <inheritdoc />
14+
public abstract class LegacyReplaceDataTypeArtifactMigratorBase : ReplaceDataTypeArtifactMigratorBase
15+
{
16+
private readonly IConfigurationEditorJsonSerializer _configurationEditorJsonSerializer;
17+
18+
/// <summary>
19+
/// Initializes a new instance of the <see cref="LegacyReplaceDataTypeArtifactMigratorBase" /> class.
20+
/// </summary>
21+
/// <param name="fromEditorAlias">The editor alias to migrate from.</param>
22+
/// <param name="toEditorAlias">The editor alias to migrate to.</param>
23+
/// <param name="toEditorUiAlias">The editor UI alias to migrate to.</param>
24+
/// <param name="propertyEditors">The property editors.</param>
25+
/// <param name="configurationEditorJsonSerializer">The configuration editor JSON serializer.</param>
26+
protected LegacyReplaceDataTypeArtifactMigratorBase(string fromEditorAlias, string toEditorAlias, string toEditorUiAlias, PropertyEditorCollection propertyEditors, IConfigurationEditorJsonSerializer configurationEditorJsonSerializer)
27+
: base(fromEditorAlias, toEditorAlias, toEditorUiAlias, propertyEditors, configurationEditorJsonSerializer)
28+
=> _configurationEditorJsonSerializer = configurationEditorJsonSerializer;
29+
30+
/// <summary>
31+
/// Replaces the old key with a new key.
32+
/// </summary>
33+
/// <param name="configuration">The configuration.</param>
34+
/// <param name="oldKey">The old key.</param>
35+
/// <param name="newKey">The new key.</param>
36+
protected static void ReplaceKey(ref IDictionary<string, object> configuration, string oldKey, string newKey)
37+
{
38+
if (configuration.Remove(oldKey, out var value))
39+
{
40+
configuration[newKey] = value;
41+
}
42+
}
43+
44+
/// <summary>
45+
/// Replaces the UDI value with the GUID.
46+
/// </summary>
47+
/// <param name="configuration">The configuration.</param>
48+
/// <param name="key">The key.</param>
49+
/// <param name="keepInvalid">If set to <c>true</c> keeps the invalid value.</param>
50+
protected static void ReplaceUdiWithGuid(ref IDictionary<string, object> configuration, string key, bool keepInvalid = false)
51+
{
52+
if (configuration.TryGetValue(key, out var value))
53+
{
54+
if (value is string udi && UdiParser.TryParse(udi, out GuidUdi? guidUdi))
55+
{
56+
configuration[key] = guidUdi.Guid;
57+
}
58+
else if (keepInvalid is false && (value is not string guid || Guid.TryParse(guid, out _) is false))
59+
{
60+
configuration.Remove(key);
61+
}
62+
}
63+
}
64+
65+
/// <summary>
66+
/// Replaces the integer value with a boolean (defaults to false).
67+
/// </summary>
68+
/// <param name="configuration">The configuration.</param>
69+
/// <param name="key">The key.</param>
70+
protected static void ReplaceIntegerWithBoolean(ref IDictionary<string, object> configuration, string key)
71+
{
72+
if (configuration.TryGetValue(key, out var value) &&
73+
value is not bool)
74+
{
75+
configuration[key] = value?.ToString()?.ToLowerInvariant() switch
76+
{
77+
"1" or "true" => true,
78+
_ => false,
79+
};
80+
}
81+
}
82+
83+
/// <summary>
84+
/// Replaces the string value with an integer.
85+
/// </summary>
86+
/// <param name="configuration">The configuration.</param>
87+
/// <param name="key">The key.</param>
88+
/// <param name="keepInvalid">If set to <c>true</c> keeps the invalid value.</param>
89+
protected static void ReplaceStringWithInteger(ref IDictionary<string, object> configuration, string key, bool keepInvalid = false)
90+
{
91+
if (configuration.TryGetValue(key, out var value) &&
92+
value is not int)
93+
{
94+
if (value is string stringValue &&
95+
int.TryParse(stringValue, out int intValue))
96+
{
97+
configuration[key] = intValue;
98+
}
99+
else if (keepInvalid is false)
100+
{
101+
configuration.Remove(key);
102+
}
103+
}
104+
}
105+
106+
/// <summary>
107+
/// Replaces the value list array with a string array.
108+
/// </summary>
109+
/// <param name="configuration">The configuration.</param>
110+
/// <param name="key">The key.</param>
111+
protected void ReplaceValueListArrayWithStringArray(ref IDictionary<string, object> configuration, string key)
112+
{
113+
if (TryDeserialize(ref configuration, key, out IEnumerable<LegacyValueListItem>? items))
114+
{
115+
configuration[key] = items.Select(x => x.Value).ToArray();
116+
}
117+
}
118+
119+
/// <summary>
120+
/// Replaces the UDI in the tree source ID value with the GUID.
121+
/// </summary>
122+
/// <param name="configuration">The configuration.</param>
123+
/// <param name="key">The key.</param>
124+
/// <param name="treeSourceType">The entity type of the tree source.</param>
125+
protected void ReplaceTreeSourceIdUdiWithGuid(ref IDictionary<string, object> configuration, string key, out string? treeSourceType)
126+
{
127+
if (TryDeserialize(ref configuration, key, out LegacyTreeSource? treeSource))
128+
{
129+
configuration[key] = new TreeSource()
130+
{
131+
Type = treeSource.Type,
132+
Id = treeSource.Id?.Guid,
133+
DynamicRoot = treeSource.DynamicRoot,
134+
};
135+
}
136+
137+
treeSourceType = treeSource?.Type;
138+
}
139+
140+
/// <summary>
141+
/// Replaces the aliases with keys.
142+
/// </summary>
143+
/// <param name="configuration">The configuration.</param>
144+
/// <param name="key">The key.</param>
145+
/// <param name="getKeyByAlias">The delegate to get the key by alias.</param>
146+
protected static void ReplaceAliasesWithKeys(ref IDictionary<string, object> configuration, string key, Func<string, Guid?> getKeyByAlias)
147+
{
148+
if (configuration.TryGetValue(key, out var value) &&
149+
value is string stringValue)
150+
{
151+
configuration[key] = string.Join(',', stringValue.Split(Constants.CharArrays.Comma).Select(getKeyByAlias).OfType<Guid>());
152+
}
153+
}
154+
155+
/// <summary>
156+
/// Attempts to JSON deserialize the value.
157+
/// </summary>
158+
/// <typeparam name="T">The type to deserialize to.</typeparam>
159+
/// <param name="configuration">The configuration.</param>
160+
/// <param name="key">The key.</param>
161+
/// <param name="value">The value.</param>
162+
/// <returns>
163+
/// <c>true</c> if the value was deserialized; otherwise, <c>false</c>.
164+
/// </returns>
165+
protected bool TryDeserialize<T>(ref IDictionary<string, object> configuration, string key, [NotNullWhen(true)] out T? value)
166+
{
167+
if (configuration.TryGetValue(key, out var configurationValue) &&
168+
configurationValue?.ToString() is string stringValue &&
169+
stringValue.DetectIsJson())
170+
{
171+
value = _configurationEditorJsonSerializer.Deserialize<T>(stringValue);
172+
}
173+
else
174+
{
175+
value = default;
176+
}
177+
178+
return value is not null;
179+
}
180+
181+
private sealed class LegacyValueListItem
182+
{
183+
public required string Value { get; set; }
184+
}
185+
186+
private sealed class LegacyTreeSource
187+
{
188+
public required string Type { get; set; }
189+
public GuidUdi? Id { get; set; }
190+
public object? DynamicRoot { get; set; }
191+
}
192+
193+
private sealed class TreeSource
194+
{
195+
public required string Type { get; set; }
196+
public Guid? Id { get; set; }
197+
public object? DynamicRoot { get; set; }
198+
}
199+
}
Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
using System;
12
using System.Collections.Generic;
2-
using Umbraco.Cms.Core;
33
using Umbraco.Cms.Core.PropertyEditors;
44
using Umbraco.Cms.Core.Semver;
55
using Umbraco.Cms.Core.Serialization;
6+
using Umbraco.Deploy.Core;
67
using Umbraco.Deploy.Infrastructure.Artifacts;
78
using Umbraco.Deploy.Infrastructure.Migrators;
89

@@ -11,9 +12,10 @@ namespace Umbraco.Deploy.Contrib.Migrators.Legacy;
1112
/// <summary>
1213
/// Migrates the <see cref="DataTypeArtifact" /> to update the <see cref="EditorAlias" /> editor configuration.
1314
/// </summary>
15+
[Obsolete("This has been replaced by ReplaceMediaPickerDataTypeArtifactMigrator and DefaultLegacyDataTypeConfigurationArtifactMigrator in Deploy.")]
1416
public class MediaPickerDataTypeArtifactMigrator : DataTypeConfigurationArtifactMigratorBase
1517
{
16-
private const string EditorAlias = "Umbraco.MediaPicker"; // TODO: Use DeployConstants.PropertyEditors.Legacy.Aliases.MediaPicker once made public
18+
private const string EditorAlias = DeployConstants.PropertyEditors.Legacy.Aliases.MediaPicker;
1719

1820
/// <summary>
1921
/// Initializes a new instance of the <see cref="MediaPickerDataTypeArtifactMigrator" /> class.
@@ -26,14 +28,5 @@ public MediaPickerDataTypeArtifactMigrator(PropertyEditorCollection propertyEdit
2628

2729
/// <inheritdoc />
2830
protected override IDictionary<string, object>? MigrateConfiguration(IDictionary<string, object> fromConfiguration)
29-
{
30-
if (fromConfiguration.TryGetValue("startNodeId", out var startNodeIdValue) &&
31-
(startNodeIdValue?.ToString() is not string startNodeId || !UdiParser.TryParse(startNodeId, out _)))
32-
{
33-
// Remove invalid start node ID
34-
fromConfiguration.Remove("startNodeId");
35-
}
36-
37-
return fromConfiguration;
38-
}
31+
=> fromConfiguration;
3932
}

0 commit comments

Comments
 (0)