From 4d47fb07fd21d099d0ad33daf9c7b378899a21eb Mon Sep 17 00:00:00 2001 From: Benjamin Carleski Date: Mon, 30 Mar 2020 15:33:18 -0700 Subject: [PATCH] Add Stacked Content to Block List migration --- .../Migrations/Upgrade/UmbracoPlan.cs | 3 + .../V_8_7_0/StackedContentToBlockList.cs | 235 ++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + 3 files changed, 239 insertions(+) create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index 7143be2b90..7cf051131f 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -7,6 +7,7 @@ using Umbraco.Core.Migrations.Upgrade.V_8_0_0; using Umbraco.Core.Migrations.Upgrade.V_8_0_1; using Umbraco.Core.Migrations.Upgrade.V_8_1_0; using Umbraco.Core.Migrations.Upgrade.V_8_6_0; +using Umbraco.Core.Migrations.Upgrade.V_8_7_0; namespace Umbraco.Core.Migrations.Upgrade { @@ -193,6 +194,8 @@ namespace Umbraco.Core.Migrations.Upgrade To("{a78e3369-8ea3-40ec-ad3f-5f76929d2b20}"); + // to 8.7.0... + To("{DFA35FA2-BFBB-433F-84E5-BD75940CDDF6}"); //FINAL } } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs new file mode 100644 index 0000000000..0714c4b632 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/StackedContentToBlockList.cs @@ -0,0 +1,235 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Umbraco.Core.Logging; +using Umbraco.Core.Migrations.PostMigrations; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_7_0 +{ + public class StackedContentToBlockList : MigrationBase + { + public StackedContentToBlockList(IMigrationContext context) : base(context) + { + } + + public override void Migrate() + { + var refreshCache = Migrate(GetDataTypes("Our.Umbraco.StackedContent"), GetKnownDocumentTypes()); + + // if some data types have been updated directly in the database (editing DataTypeDto and/or PropertyDataDto), + // bypassing the services, then we need to rebuild the cache entirely, including the umbracoContentNu table + if (refreshCache) + Context.AddPostMigration(); + } + + private List GetDataTypes(string alias) + { + var sql = Sql() + .Select() + .From() + .Where(d => d.EditorAlias == alias); + + return Database.Fetch(sql); + } + + private Dictionary GetKnownDocumentTypes() + { + var sql = Sql() + .Select(r => r.Select(x => x.NodeDto)) + .From() + .InnerJoin() + .On(c => c.NodeId, n => n.NodeId); + + var types = Database.Fetch(sql); + var map = new Dictionary(types.Count); + types.ForEach(t => map[t.NodeDto.UniqueId] = t.Alias); + return map; + } + + private bool Migrate(IEnumerable dataTypesToMigrate, Dictionary knownDocumentTypes) + { + var refreshCache = false; + + foreach (var dataType in dataTypesToMigrate) + { + if (!dataType.Configuration.IsNullOrWhiteSpace()) + { + var config = UpdateConfiguration(dataType, knownDocumentTypes); + + if (config.Blocks.Length > 0) UpdatePropertyData(dataType, config, knownDocumentTypes); + } + + UpdateDataType(dataType); + + refreshCache = true; + } + + return refreshCache; + } + + private BlockListConfiguration UpdateConfiguration(DataTypeDto dataType, Dictionary knownDocumentTypes) + { + var old = JsonConvert.DeserializeObject(dataType.Configuration); + var config = new BlockListConfiguration + { + Blocks = old.ContentTypes?.Select(t => new BlockListConfiguration.BlockConfiguration + { + Alias = knownDocumentTypes[t.IcContentTypeGuid], + Label = t.NameTemplate + }).ToArray(), + UseInlineEditingAsDefault = old.SingleItemMode == "1" || old.SingleItemMode == bool.TrueString + }; + + if (int.TryParse(old.MaxItems, out var max) && max > 0) + { + config.ValidationLimit = new BlockListConfiguration.NumberRange { Max = max }; + } + + dataType.Configuration = ConfigurationEditor.ToDatabase(config); + + return config; + } + + private void UpdatePropertyData(DataTypeDto dataType, BlockListConfiguration config, Dictionary knownDocumentTypes) + { + // get property data dtos + var propertyDataDtos = Database.Fetch(Sql() + .Select() + .From() + .InnerJoin().On((pt, pd) => pt.Id == pd.PropertyTypeId) + .InnerJoin().On((dt, pt) => dt.NodeId == pt.DataTypeId) + .Where(x => x.DataTypeId == dataType.NodeId)); + + // update dtos + var updatedDtos = propertyDataDtos.Where(x => UpdatePropertyDataDto(x, config, knownDocumentTypes)); + + // persist changes + foreach (var propertyDataDto in updatedDtos) + Database.Update(propertyDataDto); + } + + + private bool UpdatePropertyDataDto(PropertyDataDto dto, BlockListConfiguration config, Dictionary knownDocumentTypes) + { + var model = new SimpleModel(); + + if (dto != null && !dto.TextValue.IsNullOrWhiteSpace() && dto.TextValue[0] == '[') + { + var scObjs = JsonConvert.DeserializeObject(dto.TextValue); + foreach (var obj in scObjs) model.AddDataItem(obj, knownDocumentTypes); + } + + dto.TextValue = JsonConvert.SerializeObject(model); + + return true; + } + + private void UpdateDataType(DataTypeDto dataType) + { + dataType.DbType = ValueStorageType.Nvarchar.ToString(); + dataType.EditorAlias = Constants.PropertyEditors.Aliases.BlockList; + + Database.Update(dataType); + } + + private class BlockListConfiguration + { + + [ConfigurationField("blocks", "Available Blocks", "views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html", Description = "Define the available blocks.")] + public BlockConfiguration[] Blocks { get; set; } + + + [ConfigurationField("validationLimit", "Amount", "numberrange", Description = "Set a required range of blocks")] + public NumberRange ValidationLimit { get; set; } = new NumberRange(); + + public class NumberRange + { + [JsonProperty("min")] + public int? Min { get; set; } + + [JsonProperty("max")] + public int? Max { get; set; } + } + + public class BlockConfiguration + { + [JsonProperty("contentTypeAlias")] + public string Alias { get; set; } + + [JsonProperty("settingsElementTypeAlias")] + public string SettingsElementTypeAlias { get; set; } + + [JsonProperty("view")] + public string View { get; set; } + + [JsonProperty("label")] + public string Label { get; set; } + } + + [ConfigurationField("useInlineEditingAsDefault", "Inline editing mode", "boolean", Description = "Use the inline editor as the default block view")] + public bool UseInlineEditingAsDefault { get; set; } + + } + + private class StackedContentConfiguration + { + + public class StackedContentType + { + public Guid IcContentTypeGuid { get; set; } + public string NameTemplate { get; set; } + } + + public StackedContentType[] ContentTypes { get; set; } + public string EnableCopy { get; set; } + public string EnableFilter { get; set; } + public string EnablePreview { get; set; } + public string HideLabel { get; set; } + public string MaxItems { get; set; } + public string SingleItemMode { get; set; } + } + + private class SimpleModel + { + [JsonProperty("layout")] + public SimpleLayout Layout { get; } = new SimpleLayout(); + [JsonProperty("data")] + public List Data { get; } = new List(); + + public void AddDataItem(JObject obj, Dictionary 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}'"); + + obj.Remove("key"); + obj.Remove("icContentTypeGuid"); + + var udi = new GuidUdi(Constants.UdiEntityType.Element, key).ToString(); + obj["udi"] = udi; + obj["contentTypeAlias"] = ctAlias; + + Data.Add(obj); + Layout.Refs.Add(new SimpleLayout.SimpleLayoutRef { Udi = udi }); + } + + public class SimpleLayout + { + [JsonProperty(Constants.PropertyEditors.Aliases.BlockList)] + public List Refs { get; } = new List(); + + public class SimpleLayoutRef + { + [JsonProperty("udi")] + public string Udi { get; set; } + } + } + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index b6d4dedbd3..395b1ec177 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -131,6 +131,7 @@ +