From a5e4b721ba90f712d1a4ed7ca4c34c01d8a5285a Mon Sep 17 00:00:00 2001 From: Callum Whyte Date: Tue, 14 Sep 2021 14:45:16 +0100 Subject: [PATCH] Hook into DataType saving event to convert legacy editor data to Meganav --- .../Migrations/LegacyEditors.cs | 13 ++ .../Migrations/LegacyMigrationComponent.cs | 47 ++++++ .../Migrations/LegacyMigrationComposer.cs | 15 ++ .../Migrations/LegacyValueMigrator.cs | 138 ++++++++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 src/Our.Umbraco.Meganav/Migrations/LegacyEditors.cs create mode 100644 src/Our.Umbraco.Meganav/Migrations/LegacyMigrationComponent.cs create mode 100644 src/Our.Umbraco.Meganav/Migrations/LegacyMigrationComposer.cs create mode 100644 src/Our.Umbraco.Meganav/Migrations/LegacyValueMigrator.cs diff --git a/src/Our.Umbraco.Meganav/Migrations/LegacyEditors.cs b/src/Our.Umbraco.Meganav/Migrations/LegacyEditors.cs new file mode 100644 index 0000000..8cb6be4 --- /dev/null +++ b/src/Our.Umbraco.Meganav/Migrations/LegacyEditors.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Our.Umbraco.Meganav.Migrations +{ + public static class LegacyEditors + { + public static List Aliases => new List + { + "Meganav", + "Cogworks.Meganav" + }; + } +} \ No newline at end of file diff --git a/src/Our.Umbraco.Meganav/Migrations/LegacyMigrationComponent.cs b/src/Our.Umbraco.Meganav/Migrations/LegacyMigrationComponent.cs new file mode 100644 index 0000000..dcdf4c8 --- /dev/null +++ b/src/Our.Umbraco.Meganav/Migrations/LegacyMigrationComponent.cs @@ -0,0 +1,47 @@ +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Events; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; + +namespace Our.Umbraco.Meganav.Migrations +{ + internal class LegacyMigrationComponent : IComponent + { + private readonly LegacyValueMigrator _legacyValueMigrator; + + public LegacyMigrationComponent(LegacyValueMigrator legacyValueMigrator) + { + _legacyValueMigrator = legacyValueMigrator; + } + + public void Initialize() + { + DataTypeService.Saving += DataTypeService_Saving; + } + + public void Terminate() + { + DataTypeService.Saving -= DataTypeService_Saving; + } + + private void DataTypeService_Saving(IDataTypeService sender, SaveEventArgs e) + { + var dataTypes = e.SavedEntities + .Where(x => x.EditorAlias == Constants.PropertyEditorAlias) + .Where(x => + { + var existingEntity = sender.GetDataType(x.Id); + + return existingEntity != null && LegacyEditors.Aliases.Any(existingEntity.EditorAlias.InvariantContains); + }); + + foreach (var dataType in dataTypes) + { + _legacyValueMigrator.MigrateContentValues(dataType); + } + } + } +} \ No newline at end of file diff --git a/src/Our.Umbraco.Meganav/Migrations/LegacyMigrationComposer.cs b/src/Our.Umbraco.Meganav/Migrations/LegacyMigrationComposer.cs new file mode 100644 index 0000000..f19b83b --- /dev/null +++ b/src/Our.Umbraco.Meganav/Migrations/LegacyMigrationComposer.cs @@ -0,0 +1,15 @@ +using Umbraco.Core; +using Umbraco.Core.Composing; + +namespace Our.Umbraco.Meganav.Migrations +{ + public class LegacyMigrationComposer : IUserComposer + { + public void Compose(Composition composition) + { + composition.Register(); + + composition.Components().Append(); + } + } +} \ No newline at end of file diff --git a/src/Our.Umbraco.Meganav/Migrations/LegacyValueMigrator.cs b/src/Our.Umbraco.Meganav/Migrations/LegacyValueMigrator.cs new file mode 100644 index 0000000..ebae758 --- /dev/null +++ b/src/Our.Umbraco.Meganav/Migrations/LegacyValueMigrator.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Our.Umbraco.Meganav.Models; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Services; + +namespace Our.Umbraco.Meganav.Migrations +{ + internal class LegacyValueMigrator + { + private readonly IContentTypeService _contentTypeService; + private readonly IContentService _contentService; + private readonly ILogger _logger; + + public LegacyValueMigrator(IContentTypeService contentTypeService, IContentService contentService, ILogger logger) + { + _contentTypeService = contentTypeService; + _contentService = contentService; + _logger = logger; + } + + public void MigrateContentValues(IDataType dataType) + { + var contentTypes = _contentTypeService.GetAll() + .Where(x => x.PropertyTypes.Any(p => p.DataTypeId == dataType.Id)) + .ToList(); + + var contentTypeIds = contentTypes.Select(x => x.Id).ToArray(); + + var contentItems = _contentService.GetPagedOfTypes(contentTypeIds, 0, int.MaxValue, out long totalRecords, null); + + var propertyAliases = contentTypes + .SelectMany(x => x.PropertyTypes) + .Where(x => x.DataTypeId == dataType.Id) + .Select(x => x.Alias) + .Distinct() + .ToArray(); + + MigrateContentValues(contentItems, propertyAliases); + } + + public void MigrateContentValues(IEnumerable contentItems, string[] propertyAliases) + { + foreach (var content in contentItems) + { + var properties = content.Properties.Where(x => propertyAliases.InvariantContains(x.Alias)); + + foreach (var property in properties) + { + foreach (var propertyValue in property.Values) + { + try + { + var entities = ConvertToEntity(propertyValue.EditedValue); + + var value = JsonConvert.SerializeObject(entities); + + property.SetValue(value, propertyValue.Culture, propertyValue.Segment); + } + catch (Exception ex) + { + _logger.Error(ex, "Failed to migrate Meganav values for {alias} on content {id}", property.Alias, content.Id); + } + } + } + + _contentService.Save(content); + } + } + + private IEnumerable ConvertToEntity(object value) + { + if (!(value is JToken data)) + { + if (value is string stringValue) + { + data = JToken.Parse(stringValue); + } + else + { + yield break; + } + } + + foreach (var item in data.Children()) + { + var entity = new MeganavEntity + { + Title = item.Value("title"), + Target = item.Value("target"), + Visible = !item.Value("naviHide") + }; + + var id = item.Value("id"); + + GuidUdi.TryParse(item.Value("udi"), out GuidUdi udi); + + if (id > 0 || udi?.Guid != Guid.Empty) + { + var contentItem = udi != null + ? _contentService.GetById(udi.Guid) + : _contentService.GetById(id); + + if (contentItem != null) + { + entity.Udi = contentItem.GetUdi(); + } + } + else + { + entity.Url = item.Value("url"); + } + + var children = item.Value("children"); + + if (children != null) + { + entity.Children = ConvertToEntity(children); + } + + var ignoreProperties = new[] { "id", "key", "udi", "name", "title", "description", "target", "url", "children", "icon", "published", "naviHide", "culture" }; + + var settings = item.ToObject>(); + + settings.RemoveAll(x => ignoreProperties.InvariantContains(x.Key)); + + entity.Settings = settings; + + yield return entity; + } + } + } +} \ No newline at end of file