Skip to content

Instantly share code, notes, and snippets.

@skttl
Last active March 4, 2022 10:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save skttl/5b2d543c4871b5bfbd28deb93ff2c9e6 to your computer and use it in GitHub Desktop.
Save skttl/5b2d543c4871b5bfbd28deb93ff2c9e6 to your computer and use it in GitHub Desktop.
FixNestedContentKeys
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Web.WebApi;
namespace MyProject.Core
{
public class FixController : UmbracoApiController
{
private List<ContentTypeWithNestedContent> _contentTypes;
public FixController()
{
_contentTypes = GetContentTypesAndTheirNestedContentPropertyAliases();
}
[HttpGet]
public IHttpActionResult ResetKeys()
{
//return InternalServerError(new Exception("Resetting keys is disabled"));
var contentTypeIds = _contentTypes.Select(x => x.Id);
foreach (var contentTypeId in contentTypeIds)
{
var nodes = Services.ContentService.GetPagedOfType(contentTypeId, 0, int.MaxValue, out long totalNodes, null, null);
foreach (var node in nodes)
{
ResetKeys(node);
}
}
return Ok();
}
/// <summary>
/// Gets nested content values on an IContent, resets their keys and save/publish the node when done
/// </summary>
/// <param name="node"></param>
private void ResetKeys(IContent node)
{
if (node == null) return;
Logger.Info<FixController>("Resetting keys on {id}: {name}", node.Id, node.Name);
var aliases = GetNestedContentPropertyAliasesByContentTypeAlias(node.ContentType.Alias);
foreach (var alias in aliases)
{
if (node.HasProperty(alias))
{
var input = node.GetValue<string>(alias);
var output = ResetKeys(input);
if (input != output)
{
node.SetValue(alias, output);
}
}
}
if (node.IsDirty())
{
// save or publish the node
if (node.Published)
{
Services.ContentService.SaveAndPublish(node);
}
else
{
Services.ContentService.Save(node);
}
}
}
/// <summary>
/// Takes a serialized nested content value, reset its key, and loops through any nested nested content values.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private string ResetKeys(string input)
{
if (input == null) return input;
try
{
// get the nested content value, deserialize the string
var nestedItems = JsonConvert.DeserializeObject<List<Dictionary<string, string>>>(input);
// only do anything if there actually is nested content items inside
if (nestedItems != null && nestedItems.Count > 0)
{
// loop through the nested items, resetting the key
nestedItems.ForEach(item =>
{
var oldKey = item["key"];
var newKey = Guid.NewGuid().ToString();
item["key"] = newKey;
var aliases = GetNestedContentPropertyAliasesByContentTypeAlias(item["ncContentTypeAlias"]);
foreach (var alias in aliases)
{
if (item.ContainsKey(alias))
{
item[alias] = ResetKeys(item[alias]);
}
}
Logger.Info<FixController>("Converting {name} from {oldKey} to {newKey}", item["name"], oldKey, newKey);
});
return JsonConvert.SerializeObject(nestedItems);
}
}
catch (Exception ex)
{
Logger.Error<FixController>(ex);
Logger.Error<FixController>("Failed converting {input}", input);
}
return input;
}
/// <summary>
/// Gets content types containing Nested Content properties
/// </summary>
/// <returns></returns>
private List<ContentTypeWithNestedContent> GetContentTypesAndTheirNestedContentPropertyAliases()
{
var result = new List<ContentTypeWithNestedContent>();
var contentTypes = Services.ContentTypeService.GetAll();
foreach (var contentType in contentTypes)
{
var nestedContentPropertyTypes = contentType.CompositionPropertyTypes.Where(x => x.PropertyEditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.NestedContent)).ToList();
if (nestedContentPropertyTypes.Any())
{
result.Add(new ContentTypeWithNestedContent()
{
Id = contentType.Id,
Alias = contentType.Alias,
NestedContentPropertyAliases = nestedContentPropertyTypes.Select(x => x.Alias).ToList()
});
}
}
return result;
}
/// <summary>
/// Gets Nested Content property aliases by content type alias
/// </summary>
/// <param name="contentTypeAlias"></param>
/// <returns></returns>
private List<string> GetNestedContentPropertyAliasesByContentTypeAlias(string contentTypeAlias)
{
if (contentTypeAlias == null) return new List<string>();
var contentType = _contentTypes.FirstOrDefault(x => x.Alias == contentTypeAlias);
if (contentType != null)
{
return contentType.NestedContentPropertyAliases;
}
return new List<string>();
}
private class ContentTypeWithNestedContent
{
public int Id { get; set; }
public string Alias { get; set; }
public List<string> NestedContentPropertyAliases { get; set; }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment