From e434b01386e52650d17cab4fe51aa2213716c287 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 26 May 2020 14:43:57 +0100 Subject: [PATCH] Adds in NC Property Component to deal with creating/adding missing keys --- .../Compose/NestedContentPropertyComponent.cs | 174 ++++++++++++++++++ .../Compose/NestedContentPropertyComposer.cs | 9 + src/Umbraco.Web/Umbraco.Web.csproj | 2 + 3 files changed, 185 insertions(+) create mode 100644 src/Umbraco.Web/Compose/NestedContentPropertyComponent.cs create mode 100644 src/Umbraco.Web/Compose/NestedContentPropertyComposer.cs diff --git a/src/Umbraco.Web/Compose/NestedContentPropertyComponent.cs b/src/Umbraco.Web/Compose/NestedContentPropertyComponent.cs new file mode 100644 index 0000000000..28c2a288f8 --- /dev/null +++ b/src/Umbraco.Web/Compose/NestedContentPropertyComponent.cs @@ -0,0 +1,174 @@ +using System; +using System.Linq; +using Newtonsoft.Json.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; +using Umbraco.Web.PropertyEditors; + +namespace Umbraco.Web.Compose +{ + public class NestedContentPropertyComponent : IComponent + { + public void Initialize() + { + ContentService.Copying += ContentService_Copying; + ContentService.Saving += ContentService_Saving; + ContentService.Publishing += ContentService_Publishing; + } + + private void ContentService_Copying(IContentService sender, CopyEventArgs e) + { + // When a content node contains nested content property + // Check if the copied node contains a nested content + var nestedContentProps = e.Copy.Properties.Where(x => x.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.NestedContent); + + // Each NC Property on a doctype + foreach (var nestedContentProp in nestedContentProps) + { + // A NC Prop may have one or more values due to cultures + var propVals = nestedContentProp.Values; + foreach (var cultureVal in propVals) + { + // Remove keys from published value & any nested NC's + var updatedPublishedVal = CreateNewNestedContentKeys(cultureVal.PublishedValue.ToString()); + cultureVal.PublishedValue = updatedPublishedVal; + + // Remove keys from edited/draft value & any nested NC's + var updatedEditedVal = CreateNewNestedContentKeys(cultureVal.EditedValue.ToString()); + cultureVal.EditedValue = updatedEditedVal; + } + } + } + + private void ContentService_Saving(IContentService sender, ContentSavingEventArgs e) + { + // One or more content nodes could be saved in a bulk publish + foreach(var entity in e.SavedEntities) + { + // When a content node contains nested content property + // Check if the copied node contains a nested content + var nestedContentProps = entity.Properties.Where(x => x.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.NestedContent); + + // Each NC Property on a doctype + foreach (var nestedContentProp in nestedContentProps) + { + // A NC Prop may have one or more values due to cultures + var propVals = nestedContentProp.Values; + foreach (var cultureVal in propVals) + { + // Remove keys from published value & any nested NC's + var updatedPublishedVal = CreateMissingNestedContentKeys(cultureVal.PublishedValue.ToString()); + cultureVal.PublishedValue = updatedPublishedVal; + + // Remove keys from edited/draft value & any nested NC's + var updatedEditedVal = CreateMissingNestedContentKeys(cultureVal.EditedValue.ToString()); + cultureVal.EditedValue = updatedEditedVal; + } + } + } + } + + private void ContentService_Publishing(IContentService sender, ContentPublishingEventArgs e) + { + // One or more content nodes could be saved in a bulk publish + foreach (var entity in e.PublishedEntities) + { + // When a content node contains nested content property + // Check if the copied node contains a nested content + var nestedContentProps = entity.Properties.Where(x => x.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.NestedContent); + + // Each NC Property on a doctype + foreach (var nestedContentProp in nestedContentProps) + { + // A NC Prop may have one or more values due to cultures + var propVals = nestedContentProp.Values; + foreach (var cultureVal in propVals) + { + // Remove keys from published value & any nested NC's + var updatedPublishedVal = CreateMissingNestedContentKeys(cultureVal.PublishedValue.ToString()); + cultureVal.PublishedValue = updatedPublishedVal; + + // Remove keys from edited/draft value & any nested NC's + var updatedEditedVal = CreateMissingNestedContentKeys(cultureVal.EditedValue.ToString()); + cultureVal.EditedValue = updatedEditedVal; + } + } + } + } + + public void Terminate() + { + ContentService.Copying -= ContentService_Copying; + ContentService.Saving -= ContentService_Saving; + ContentService.Publishing -= ContentService_Publishing; + } + + private string CreateNewNestedContentKeys(string ncJson) + { + // Try & convert JSON to JArray (two props we will know should exist are key & ncContentTypeAlias) + var ncItems = JArray.Parse(ncJson); + + // NC prop contains one or more items/rows of things + foreach (var nestedContentItem in ncItems.Children()) + { + foreach (var ncItemProp in nestedContentItem.Properties()) + { + if(ncItemProp.Name.ToLowerInvariant() == "key") + ncItemProp.Value = Guid.NewGuid().ToString(); + + // No need to check this property for JSON - as this is a JSON prop we know + // That onyl contains the string of the doctype alias used as the NC item + if (ncItemProp.Name == NestedContentPropertyEditor.ContentTypeAliasPropertyKey) + continue; + + // As we don't know what properties in the JSON may contain the nested NC + // We are detecting if its value stores JSON to help filter the list AND that in its JSON it has ncContentTypeAlias prop + if (ncItemProp.Value.ToString().DetectIsJson() && ncItemProp.Value.ToString().Contains(NestedContentPropertyEditor.ContentTypeAliasPropertyKey)) + { + // Recurse & update this JSON property + ncItemProp.Value = CreateNewNestedContentKeys(ncItemProp.Value.ToString()); + } + } + } + + return ncItems.ToString(); + } + + private string CreateMissingNestedContentKeys(string ncJson) + { + // Try & convert JSON to JArray (two props we will know should exist are key & ncContentTypeAlias) + var ncItems = JArray.Parse(ncJson); + + // NC prop contains one or more items/rows of things + foreach (var nestedContentItem in ncItems.Children()) + { + var ncKeyProp = nestedContentItem.Properties().SingleOrDefault(x => x.Name.ToLowerInvariant() == "key"); + if(ncKeyProp == null) + { + nestedContentItem.Properties().Append(new JProperty("key", Guid.NewGuid().ToString())); + } + + foreach (var ncItemProp in nestedContentItem.Properties()) + { + // No need to check this property for JSON (Its the key OR ncContentTypeAlias) which has no JSON + if (ncItemProp.Name == NestedContentPropertyEditor.ContentTypeAliasPropertyKey || ncItemProp.Name.ToLowerInvariant() == "key") + continue; + + // As we don't know what properties in the JSON may contain the nested NC + // We are detecting if its value stores JSON to help filter the list AND that in its JSON it has ncContentTypeAlias prop + if (ncItemProp.Value.ToString().DetectIsJson() && ncItemProp.Value.ToString().Contains(NestedContentPropertyEditor.ContentTypeAliasPropertyKey)) + { + // Recurse & update this JSON property + ncItemProp.Value = CreateMissingNestedContentKeys(ncItemProp.Value.ToString()); + } + } + } + + return ncItems.ToString(); + } + } +} diff --git a/src/Umbraco.Web/Compose/NestedContentPropertyComposer.cs b/src/Umbraco.Web/Compose/NestedContentPropertyComposer.cs new file mode 100644 index 0000000000..4c9d9dee1c --- /dev/null +++ b/src/Umbraco.Web/Compose/NestedContentPropertyComposer.cs @@ -0,0 +1,9 @@ +using Umbraco.Core; +using Umbraco.Core.Composing; + +namespace Umbraco.Web.Compose +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public class NestedContentPropertyComposer : ComponentComposer, ICoreComposer + { } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e39687bed8..c06ec574c5 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -129,6 +129,7 @@ + @@ -237,6 +238,7 @@ +