Add migrations for new block editor

This commit is contained in:
Benjamin Carleski
2020-04-07 11:55:45 -07:00
parent 3bf2a17d49
commit f78e4fcdf6
5 changed files with 268 additions and 12 deletions

View File

@@ -194,6 +194,11 @@ namespace Umbraco.Core.Migrations.Upgrade
To<MissingDictionaryIndex>("{a78e3369-8ea3-40ec-ad3f-5f76929d2b20}");
// to 8.7.0...
To<StackedContentToBlockList>("{DFA35FA2-BFBB-433F-84E5-BD75940CDDF6}");
To<ColorPickerPreValues>("{711AC937-B11C-47AC-8D4A-5B8868A3C2C6}");
To<ConvertToElements>("{DA434576-3DEF-46D7-942A-CE34D7F7FB8A}");
//FINAL
}
}

View File

@@ -0,0 +1,106 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using System.Text.RegularExpressions;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0
{
public class ColorPickerPreValues : MigrationBase
{
private static readonly Regex OldPreValuesPattern1 = new Regex("\\s*{(\\s*\"[0-9]+\"\\s*:\\s*\"[0-9a-fA-F]+\"\\s*,)*\\s*\"useLabel\"\\s*:\\s*\"[01]\"\\s*}\\s*", RegexOptions.Compiled);
private static readonly Regex OldPreValuesPattern2 = new Regex("\\s*{(\\s*\"[0-9]+\"\\s*:\\s*{\\s*\"value\"\\s*:\\s*\"[0-9a-fA-F]+\"\\s*(,\\s*\"label\"\\s*:\\s*\"[^\"]*\"\\s*)?(,\\s*\"sortOrder\"\\s*:\\s*[0-9]+\\s*)?}\\s*,)*\\s*\"useLabel\"\\s*:\\s*\"[01]\"\\s*}\\s*", RegexOptions.Compiled);
public ColorPickerPreValues(IMigrationContext context) : base(context)
{
}
public override void Migrate()
{
var sql = Sql()
.Select<DataTypeDto>()
.From<DataTypeDto>()
.Where<DataTypeDto>(d => d.EditorAlias == Constants.PropertyEditors.Aliases.ColorPicker);
var dtos = Database.Fetch<DataTypeDto>(sql);
foreach (var dto in dtos)
{
if (dto.Configuration.IsNullOrWhiteSpace()) continue;
if (OldPreValuesPattern1.IsMatch(dto.Configuration)) ConvertPreValues(dto, ConvertStyle1);
else if (OldPreValuesPattern2.IsMatch(dto.Configuration)) ConvertPreValues(dto, ConvertStyle2);
else continue;
Database.Update(dto);
}
}
private void ConvertPreValues(DataTypeDto dto, Func<int, JToken, ItemValue> converter)
{
var obj = JObject.Parse(dto.Configuration);
var config = new ColorPickerConfiguration();
var id = 0;
foreach (var prop in obj.Properties())
{
if (prop.Name.ToLowerInvariant() == "uselabel")
{
config.UseLabel = prop.Value.ToString() == "1";
}
else
{
id++;
config.Items.Add(new ValueListConfiguration.ValueListItem
{
Id = id,
Value = JsonConvert.SerializeObject(converter(id, prop.Value))
});
}
}
dto.Configuration = JsonConvert.SerializeObject(config);
}
private ItemValue ConvertStyle1(int index, JToken token)
{
var value = token.ToString();
return new ItemValue
{
Color = value,
Label = value,
SortOrder = index
};
}
private ItemValue ConvertStyle2(int index, JToken token)
{
var obj = (JObject)token;
var value = obj["value"].ToString();
var label = obj["label"]?.ToString();
var order = obj["sortOrder"]?.ToString();
return new ItemValue
{
Color = value,
Label = label.IsNullOrWhiteSpace() ? value : label,
SortOrder = int.TryParse(order, out var o) ? o : index
};
}
private class ItemValue
{
[JsonProperty("value")]
public string Color { get; set; }
[JsonProperty("label")]
public string Label { get; set; }
[JsonProperty("sortOrder")]
public int SortOrder { get; set; }
}
}
}

View File

@@ -0,0 +1,92 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0
{
public class ConvertToElements : MigrationBase
{
public ConvertToElements(IMigrationContext context) : base(context)
{
}
public override void Migrate()
{
// Get all document type IDs by alias
var docTypes = Database.Fetch<ContentTypeDto>();
var docTypeMap = new Dictionary<string, int>(docTypes.Count);
docTypes.ForEach(d => docTypeMap[d.Alias] = d.NodeId);
// Find all Nested Content or Block List data types
var dataTypes = GetDataTypes(Constants.PropertyEditors.Aliases.NestedContent, Constants.PropertyEditors.Aliases.BlockList);
// Find all document types listed in each
var elementTypeIds = dataTypes.SelectMany(d => GetDocTypeIds(d.Configuration, docTypeMap)).ToList();
// Find all compositions those document types use
var parentElementTypeIds = Database.Fetch<ContentType2ContentTypeDto>(Sql()
.Select<ContentType2ContentTypeDto>()
.From<ContentType2ContentTypeDto>()
.WhereIn<ContentType2ContentTypeDto>(c => c.ChildId, elementTypeIds)
).Select(c => c.ParentId);
elementTypeIds = elementTypeIds.Union(parentElementTypeIds).ToList();
// Convert all those document types to element type
foreach (var docType in docTypes)
{
if (!elementTypeIds.Contains(docType.NodeId)) continue;
docType.IsElement = true;
Database.Update(docType);
}
}
private List<DataTypeDto> GetDataTypes(params string[] aliases)
{
var sql = Sql()
.Select<DataTypeDto>()
.From<DataTypeDto>()
.WhereIn<DataTypeDto>(d => d.EditorAlias, aliases);
return Database.Fetch<DataTypeDto>(sql);
}
private IEnumerable<int> GetDocTypeIds(string configuration, Dictionary<string, int> idMap)
{
if (configuration.IsNullOrWhiteSpace() || configuration[0] != '{') return Enumerable.Empty<int>();
var obj = JObject.Parse(configuration);
if (obj["contentTypes"] is JArray ncArr)
{
var arr = ncArr.ToObject<ContentType[]>();
return arr.Select(i => idMap.TryGetValue(i.Alias, out var id) ? id : 0).Where(i => i != 0);
}
else if (obj["blocks"] is JArray blArr)
{
var arr = blArr.ToObject<BlockConfiguration[]>();
return arr.Select(i => idMap.TryGetValue(i.Alias, out var id) ? id : 0).Where(i => i != 0);
}
return Enumerable.Empty<int>();
}
public class ContentType
{
[JsonProperty("ncAlias")]
public string Alias { get; set; }
}
public class BlockConfiguration
{
[JsonProperty("contentTypeAlias")]
public string Alias { get; set; }
}
}
}

View File

@@ -20,6 +20,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0
public override void Migrate()
{
// Convert all Stacked Content properties to Block List properties, both in the data types and in the property data
var refreshCache = Migrate(GetDataTypes("Our.Umbraco.StackedContent"), GetKnownDocumentTypes());
// if some data types have been updated directly in the database (editing DataTypeDto and/or PropertyDataDto),
@@ -38,7 +39,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0
return Database.Fetch<DataTypeDto>(sql);
}
private Dictionary<Guid, string> GetKnownDocumentTypes()
private Dictionary<Guid, KnownContentType> GetKnownDocumentTypes()
{
var sql = Sql()
.Select<ContentTypeDto>(r => r.Select(x => x.NodeDto))
@@ -47,12 +48,40 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0
.On<ContentTypeDto, NodeDto>(c => c.NodeId, n => n.NodeId);
var types = Database.Fetch<ContentTypeDto>(sql);
var map = new Dictionary<Guid, string>(types.Count);
types.ForEach(t => map[t.NodeDto.UniqueId] = t.Alias);
return map;
var typeMap = new Dictionary<int, ContentTypeDto>(types.Count);
types.ForEach(t => typeMap[t.NodeId] = t);
sql = Sql()
.Select<ContentType2ContentTypeDto>()
.From<ContentType2ContentTypeDto>();
var joins = Database.Fetch<ContentType2ContentTypeDto>(sql);
// Find all relationships between types, either inherited or composited
var joinLk = joins
.Union(types
.Where(t => typeMap.ContainsKey(t.NodeDto.ParentId))
.Select(t => new ContentType2ContentTypeDto { ChildId = t.NodeId, ParentId = t.NodeDto.ParentId }))
.ToLookup(j => j.ChildId, j => j.ParentId);
sql = Sql()
.Select<PropertyTypeDto>(r => r.Select(x => x.DataTypeDto))
.From<PropertyTypeDto>()
.InnerJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(c => c.DataTypeId, n => n.NodeId)
.Where<DataTypeDto>(d => d.EditorAlias == Constants.PropertyEditors.Aliases.NestedContent);
var props = Database.Fetch<PropertyTypeDto>(sql);
// Get all nested content property aliases by content type ID
var propLk = props.ToLookup(p => p.ContentTypeId, p => p.Alias);
var knownMap = new Dictionary<Guid, KnownContentType>(types.Count);
types.ForEach(t => knownMap[t.NodeDto.UniqueId] = new KnownContentType
{
Alias = t.Alias,
NestedContentProperties = propLk[t.NodeId].Union(joinLk[t.NodeId].SelectMany(r => propLk[r])).ToArray()
});
return knownMap;
}
private bool Migrate(IEnumerable<DataTypeDto> dataTypesToMigrate, Dictionary<Guid, string> knownDocumentTypes)
private bool Migrate(IEnumerable<DataTypeDto> dataTypesToMigrate, Dictionary<Guid, KnownContentType> knownDocumentTypes)
{
var refreshCache = false;
@@ -73,14 +102,14 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0
return refreshCache;
}
private BlockListConfiguration UpdateConfiguration(DataTypeDto dataType, Dictionary<Guid, string> knownDocumentTypes)
private BlockListConfiguration UpdateConfiguration(DataTypeDto dataType, Dictionary<Guid, KnownContentType> knownDocumentTypes)
{
var old = JsonConvert.DeserializeObject<StackedContentConfiguration>(dataType.Configuration);
var config = new BlockListConfiguration
{
Blocks = old.ContentTypes?.Select(t => new BlockListConfiguration.BlockConfiguration
{
Alias = knownDocumentTypes[t.IcContentTypeGuid],
Alias = knownDocumentTypes[t.IcContentTypeGuid].Alias,
Label = t.NameTemplate
}).ToArray(),
UseInlineEditingAsDefault = old.SingleItemMode == "1" || old.SingleItemMode == bool.TrueString
@@ -96,7 +125,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0
return config;
}
private void UpdatePropertyData(DataTypeDto dataType, BlockListConfiguration config, Dictionary<Guid, string> knownDocumentTypes)
private void UpdatePropertyData(DataTypeDto dataType, BlockListConfiguration config, Dictionary<Guid, KnownContentType> knownDocumentTypes)
{
// get property data dtos
var propertyDataDtos = Database.Fetch<PropertyDataDto>(Sql()
@@ -115,7 +144,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0
}
private bool UpdatePropertyDataDto(PropertyDataDto dto, BlockListConfiguration config, Dictionary<Guid, string> knownDocumentTypes)
private bool UpdatePropertyDataDto(PropertyDataDto dto, BlockListConfiguration config, Dictionary<Guid, KnownContentType> knownDocumentTypes)
{
var model = new SimpleModel();
@@ -202,18 +231,33 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0
[JsonProperty("data")]
public List<JObject> Data { get; } = new List<JObject>();
public void AddDataItem(JObject obj, Dictionary<Guid, string> knownDocumentTypes)
public void AddDataItem(JObject obj, Dictionary<Guid, KnownContentType> knownDocumentTypes)
{
if (!Guid.TryParse(obj["key"].ToString(), out var key)) throw new ArgumentException("Could not find a valid key in the data item");
if (!Guid.TryParse(obj["icContentTypeGuid"].ToString(), out var ctGuid)) throw new ArgumentException("Could not find a valid content type GUID in the data item");
if (!knownDocumentTypes.TryGetValue(ctGuid, out var ctAlias)) throw new ArgumentException($"Unknown content type GUID '{ctGuid}'");
if (!knownDocumentTypes.TryGetValue(ctGuid, out var ct)) throw new ArgumentException($"Unknown content type GUID '{ctGuid}'");
obj.Remove("key");
obj.Remove("icContentTypeGuid");
var udi = new GuidUdi(Constants.UdiEntityType.Element, key).ToString();
obj["udi"] = udi;
obj["contentTypeAlias"] = ctAlias;
obj["contentTypeAlias"] = ct.Alias;
if (ct.NestedContentProperties != null && ct.NestedContentProperties.Length > 0)
{
// Nested content inside a stacked content item used to be stored as a deserialized string of the JSON array
// Now we store the content as the raw JSON array, so we need to convert from the string form to the array
foreach (var prop in ct.NestedContentProperties)
{
var val = obj[prop];
var value = val?.ToString();
if (val != null && val.Type == JTokenType.String && !value.IsNullOrWhiteSpace() && value[0] == '[')
obj[prop] = JArray.Parse(value);
else if (val.Type != JTokenType.Array)
obj[prop] = new JArray();
}
}
Data.Add(obj);
Layout.Refs.Add(new SimpleLayout.SimpleLayoutRef { Udi = udi });
@@ -231,5 +275,11 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0
}
}
}
private class KnownContentType
{
public string Alias { get; set; }
public string[] NestedContentProperties { get; set; }
}
}
}

View File

@@ -131,6 +131,9 @@
<Compile Include="Migrations\Upgrade\V_8_0_0\Models\ContentTypeDto80.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\Models\PropertyDataDto80.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\Models\PropertyTypeDto80.cs" />
<Compile Include="Migrations\Upgrade\V_8_7_0\ColorPickerPreValues.cs" />
<Compile Include="Migrations\Upgrade\V_8_7_0\ConvertToElements.cs" />
<Compile Include="Migrations\Upgrade\V_8_7_0\StackedContentToBlockList.cs" />
<Compile Include="Models\Blocks\IBlockEditorDataHelper.cs" />
<Compile Include="Models\InstallLog.cs" />
<Compile Include="Persistence\Repositories\IInstallationRepository.cs" />