diff --git a/src/Umbraco.Cms.Api.Management/Mapping/DataType/DataTypeViewModelMapDefinition.cs b/src/Umbraco.Cms.Api.Management/Mapping/DataType/DataTypeViewModelMapDefinition.cs index 380400fc00..71068e9cee 100644 --- a/src/Umbraco.Cms.Api.Management/Mapping/DataType/DataTypeViewModelMapDefinition.cs +++ b/src/Umbraco.Cms.Api.Management/Mapping/DataType/DataTypeViewModelMapDefinition.cs @@ -17,7 +17,7 @@ public class DataTypeViewModelMapDefinition : IMapDefinition target.Id = source.Key; target.Name = source.Name ?? string.Empty; target.EditorAlias = source.EditorAlias; - target.EditorUiAlias = source.EditorUiAlias; + target.EditorUiAlias = source.EditorUiAlias ?? source.EditorAlias; target.IsDeletable = source.IsDeletableDataType(); target.CanIgnoreStartNodes = source.IsBuildInDataType() is false; diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index d217f4f4d2..ef6fbdd47e 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -32820,6 +32820,7 @@ "CreateDataTypeRequestModel": { "required": [ "editorAlias", + "editorUiAlias", "name", "values" ], @@ -32834,8 +32835,7 @@ "type": "string" }, "editorUiAlias": { - "type": "string", - "nullable": true + "type": "string" }, "values": { "type": "array", @@ -34622,6 +34622,7 @@ "required": [ "canIgnoreStartNodes", "editorAlias", + "editorUiAlias", "id", "isDeletable", "name", @@ -34638,8 +34639,7 @@ "type": "string" }, "editorUiAlias": { - "type": "string", - "nullable": true + "type": "string" }, "values": { "type": "array", @@ -42047,6 +42047,7 @@ "UpdateDataTypeRequestModel": { "required": [ "editorAlias", + "editorUiAlias", "name", "values" ], @@ -42061,8 +42062,7 @@ "type": "string" }, "editorUiAlias": { - "type": "string", - "nullable": true + "type": "string" }, "values": { "type": "array", diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/DataType/DataTypeModelBase.cs b/src/Umbraco.Cms.Api.Management/ViewModels/DataType/DataTypeModelBase.cs index dca2958b81..f707043da8 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/DataType/DataTypeModelBase.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/DataType/DataTypeModelBase.cs @@ -10,7 +10,7 @@ public abstract class DataTypeModelBase [Required] public string EditorAlias { get; set; } = string.Empty; - public string? EditorUiAlias { get; set; } + public string EditorUiAlias { get; set; } = string.Empty; public IEnumerable Values { get; set; } = Enumerable.Empty(); } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index b48a68b5fb..1789a82f8b 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -80,5 +80,6 @@ public class UmbracoPlan : MigrationPlan To("{302DE171-6D83-4B6B-B3C0-AC8808A16CA1}"); To("{8184E61D-ECBA-4AAA-B61B-D7A82EB82EB7}"); To("{E261BF01-2C7F-4544-BAE7-49D545B21D68}"); + To("{5A2EF07D-37B4-49D5-8E9B-3ED01877263B}"); } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_14_0_0/AddEditorUiToDataType.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_14_0_0/AddEditorUiToDataType.cs new file mode 100644 index 0000000000..c178f24088 --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_14_0_0/AddEditorUiToDataType.cs @@ -0,0 +1,136 @@ +using System.Text.Json.Serialization; +using Microsoft.Extensions.Logging; +using NPoco; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.Persistence; +using Umbraco.Cms.Infrastructure.Persistence.Dtos; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_14_0_0; + +public class AddEditorUiToDataType : MigrationBase +{ + private readonly IKeyValueService _keyValueService; + private readonly IJsonSerializer _jsonSerializer; + private readonly ILogger _logger; + + public AddEditorUiToDataType(IMigrationContext context, IKeyValueService keyValueService, IJsonSerializer jsonSerializer, ILogger logger) + : base(context) + { + _keyValueService = keyValueService; + _jsonSerializer = jsonSerializer; + _logger = logger; + } + + protected override void Migrate() + { + var dataEditorSplitCollectionData = _keyValueService.GetValue("migrateDataEditorSplitCollectionData"); + if (dataEditorSplitCollectionData.IsNullOrWhiteSpace()) + { + return; + } + + DataTypeEditorAliasMigrationData[]? migrationData = _jsonSerializer.Deserialize(dataEditorSplitCollectionData); + if (migrationData?.Any() is not true) + { + _logger.LogError("No migration data was found for the data editor split. Please make sure you're upgrading from the latest V13. Any data types based on custom property editors may not work correctly."); + return; + } + + Sql sql = Sql() + .Select() + .AndSelect() + .From() + .InnerJoin() + .On(left => left.NodeId, right => right.NodeId) + .Where(x => x.EditorUiAlias == null); + + List dataTypeDtos = Database.Fetch(sql); + foreach (DataTypeDto dataTypeDto in dataTypeDtos) + { + dataTypeDto.EditorUiAlias = dataTypeDto.EditorAlias switch + { + Constants.PropertyEditors.Aliases.BlockList => "Umb.PropertyEditorUi.BlockList", + Constants.PropertyEditors.Aliases.BlockGrid => "Umb.PropertyEditorUi.BlockGrid", + Constants.PropertyEditors.Aliases.CheckBoxList => "Umb.PropertyEditorUi.CheckBoxList", + Constants.PropertyEditors.Aliases.ColorPicker => "Umb.PropertyEditorUi.ColorPicker", + Constants.PropertyEditors.Aliases.ColorPickerEyeDropper => "Umb.PropertyEditorUi.EyeDropper", + Constants.PropertyEditors.Aliases.ContentPicker => "Umb.PropertyEditorUi.DocumentPicker", + Constants.PropertyEditors.Aliases.DateTime => "Umb.PropertyEditorUi.DatePicker", + Constants.PropertyEditors.Aliases.DropDownListFlexible => "Umb.PropertyEditorUi.Dropdown", + Constants.PropertyEditors.Aliases.ImageCropper => "Umb.PropertyEditorUi.ImageCropper", + Constants.PropertyEditors.Aliases.Integer => "Umb.PropertyEditorUi.Integer", + Constants.PropertyEditors.Aliases.Decimal => "Umb.PropertyEditorUi.Decimal", + Constants.PropertyEditors.Aliases.ListView => "Umb.PropertyEditorUi.Collection", + Constants.PropertyEditors.Aliases.MediaPicker3 => "Umb.PropertyEditorUi.MediaPicker", + Constants.PropertyEditors.Aliases.MemberPicker => "Umb.PropertyEditorUi.MemberPicker", + Constants.PropertyEditors.Aliases.MemberGroupPicker => "Umb.PropertyEditorUi.MemberGroupPicker", + Constants.PropertyEditors.Aliases.MultiNodeTreePicker => "Umb.PropertyEditorUi.TreePicker", + Constants.PropertyEditors.Aliases.MultipleTextstring => "Umb.PropertyEditorUi.MultipleTextString", + Constants.PropertyEditors.Aliases.Label => "Umb.PropertyEditorUi.Label", + Constants.PropertyEditors.Aliases.RadioButtonList => "Umb.PropertyEditorUi.RadioButtonList", + Constants.PropertyEditors.Aliases.Slider => "Umb.PropertyEditorUi.Slider", + Constants.PropertyEditors.Aliases.Tags => "Umb.PropertyEditorUi.Tags", + Constants.PropertyEditors.Aliases.TextBox => "Umb.PropertyEditorUi.TextBox", + Constants.PropertyEditors.Aliases.TextArea => "Umb.PropertyEditorUi.TextArea", + Constants.PropertyEditors.Aliases.RichText => "Umb.PropertyEditorUi.TinyMCE", + Constants.PropertyEditors.Aliases.TinyMce => "Umb.PropertyEditorUi.TinyMCE", + Constants.PropertyEditors.Aliases.Boolean => "Umb.PropertyEditorUi.Toggle", + Constants.PropertyEditors.Aliases.MarkdownEditor => "Umb.PropertyEditorUi.MarkdownEditor", + Constants.PropertyEditors.Aliases.UserPicker => "Umb.PropertyEditorUi.UserPicker", + Constants.PropertyEditors.Aliases.UploadField => "Umb.PropertyEditorUi.UploadField", + Constants.PropertyEditors.Aliases.EmailAddress => "Umb.PropertyEditorUi.EmailAddress", + Constants.PropertyEditors.Aliases.MultiUrlPicker => "Umb.PropertyEditorUi.MultiUrlPicker", + _ => null + }; + + if (dataTypeDto.EditorUiAlias is null) + { + DataTypeEditorAliasMigrationData? dataTypeMigrationData = migrationData.FirstOrDefault(md => md.DataTypeId == dataTypeDto.NodeId); + if (dataTypeMigrationData is not null) + { + // the V13 "data type split data collector" works like this: + // - if .EditorUiAlias is set, the editor is based on manifests and should use one of the "Umbraco.Plain" options as .EditorAlias + // - if .EditorUiAlias is not set, the editor is based on code and should use its own alias as .EditorUiAlias + // unfortunately there is an issue with the migrator, in that it does not handle manifest based editors using valueType=TEXT, + // but with the above logic in mind, we can work around that :) + if (dataTypeMigrationData.EditorUiAlias.IsNullOrWhiteSpace()) + { + // editor based on code + dataTypeDto.EditorUiAlias = dataTypeDto.EditorAlias; + } + else + { + // editor based on manifests + dataTypeDto.EditorUiAlias = dataTypeMigrationData.EditorUiAlias; + dataTypeDto.EditorAlias = dataTypeMigrationData.EditorAlias?.NullOrWhiteSpaceAsNull() + ?? "Umbraco.Plain.String"; + } + } + else + { + _logger.LogWarning("No migration data was found for the data editor split for data type {name} ({editorAlias} - {id}). Please make sure you're upgrading from the latest V13. The affected data type may not work correctly.", dataTypeDto.NodeDto.Text, dataTypeDto.EditorAlias, dataTypeDto.NodeId); + + // we *need* an EditorUiAlias - default to the editor alias (let the client handle later) + dataTypeDto.EditorUiAlias = dataTypeDto.EditorAlias; + } + } + + Database.Update(dataTypeDto); + } + } + + private class DataTypeEditorAliasMigrationData + { + [JsonPropertyName("DataTypeId")] + public int DataTypeId { get; set; } + + [JsonPropertyName("EditorUiAlias")] + public string? EditorUiAlias { get; init; } + + [JsonPropertyName("EditorAlias")] + public string? EditorAlias { get; init; } + } +}