Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Migrate Ids to Udis for Umbraco.MultiNodeTreePicker
using System;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Web;
namespace Sniper.Umbraco
{
public static class MultiNodeTreePickerIdToUdiMigrator
{
private class Row
{
public int id { get; set; }
public int contentNodeId { get; set; }
public string alias { get; set; }
public string dataNvarchar { get; set; }
public string dataNtext { get; set; }
public int? dataInt { get; set; }
public string preValue { get; set; }
}
public static void MigrateIdsToUdis(ApplicationContext applicationContext)
{
var database = applicationContext.DatabaseContext.Database;
string sql = @"SELECT cmsPropertyData.id, cmsPropertyData.contentNodeId, cmsPropertyType.alias, dataNvarchar, dataNtext, dataInt, preValue=cmsDataTypePreValues.value, dbo.cmsDocument.*
FROM dbo.cmsPropertyData
JOIN dbo.cmsPropertyType ON cmsPropertyType.id = cmsPropertyData.propertytypeid
JOIN dbo.cmsDataType ON cmsDataType.nodeId = cmsPropertyType.dataTypeId
JOIN dbo.cmsDataTypePreValues ON cmsDataTypePreValues.datatypeNodeId = cmsDataType.nodeId AND cmsDataTypePreValues.alias = 'startNode'
JOIN dbo.cmsContentVersion ON cmsContentVersion.VersionId = cmsPropertyData.versionId
JOIN dbo.umbracoNode ON umbracoNode.id = dbo.cmsContentVersion.ContentId
JOIN dbo.cmsDocument ON cmsDocument.nodeId = umbracoNode.id
WHERE cmsDataType.propertyEditorAlias IN ('Umbraco.MultiNodeTreePicker2')
AND (dataNvarchar IS NOT NULL OR dataInt IS NOT NULL)
AND (dbo.cmsDocument.published=1 OR dbo.cmsDocument.newest=1 OR dbo.cmsDocument.updateDate > (SELECT updateDate FROM dbo.cmsDocument innerDoc WHERE innerDoc.nodeId = dbo.cmsDocument.nodeId AND innerDoc.published=1 AND newest=1))
ORDER BY contentNodeId, dbo.cmsDataType.propertyEditorAlias";
var treePickerDataToMigrate = database.Query<Row>(sql).ToList();
if (treePickerDataToMigrate.Any())
{
foreach (var propertyData in treePickerDataToMigrate)
{
int[] ids;
if (propertyData.dataInt != null)
{
ids = new[] { propertyData.dataInt.Value };
}
else if (propertyData.dataNvarchar != null)
{
ids = propertyData.dataNvarchar.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();
}
else
{
LogHelper.Info(typeof(MultiNodeTreePickerIdToUdiMigrator), () => $"MigrateIdsToUdis (node id: {propertyData.contentNodeId}) skipping property {propertyData.alias} - null dataInt and null dataNvarchar");
continue;
}
string csv = string.Join(",", ids);
var type = propertyData.preValue.Contains("\"type\": \"content\"") ? "document" : "media";
Guid[] uniqueIds = null;
string uniqueIdsCsv = string.Empty;
if (ids.Any())
{
uniqueIds = database.Query<Guid>($"SELECT uniqueId FROM umbracoNode WHERE id IN ({csv})").ToArray();
uniqueIdsCsv = string.Join(",", uniqueIds.Select(id => $"umb://{type}/{id:N}"));
}
LogHelper.Info(typeof(MultiNodeTreePickerIdToUdiMigrator), () => $"MigrateIdsToUdis (node id: {propertyData.contentNodeId}) converting property {propertyData.alias} from {csv} to {uniqueIdsCsv}");
database.Execute("UPDATE cmsPropertyData SET dataInt=NULL, dataNvarchar=NULL, dataNtext=@0 WHERE id=@1", uniqueIdsCsv, propertyData.id);
}
LogHelper.Info(typeof(MultiNodeTreePickerIdToUdiMigrator), () => $"MigrateIdsToUdis: republishing all nodes to update xml cache (equivalent to /umbraco/dialogs/republish.aspx?xml=true)");
var contentService = ApplicationContext.Current.Services.ContentService;
contentService.RePublishAll();
umbraco.library.RefreshContent();
LogHelper.Info(typeof(MultiNodeTreePickerIdToUdiMigrator), () => $"MigrateIdsToUdis: republishing complete");
}
}
}
}
using System.Configuration;
using Umbraco.Core;
namespace Sniper.Umbraco
{
internal class StartupHandler : ApplicationEventHandler
{
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
// migrate ids to udis
if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["Sniper.Umbraco.EnableMultiNodeTreePickerIdToUdiMigrator"]) && bool.TryParse(ConfigurationManager.AppSettings["Sniper.Umbraco.EnableMultiNodeTreePickerIdToUdiMigrator"], out bool enabled) && enabled)
{
MultiNodeTreePickerIdToUdiMigrator.MigrateIdsToUdis(applicationContext);
}
}
}
}
@kiasyn
Copy link
Author

kiasyn commented Aug 9, 2018

If you are migrating your treepickers from Umbraco.MultiNodeTreePicker to the new Umbraco.MultiNodeTreePicker2 you will need to manually update the data.

As this hooks into start up handler, you may need to restart your app pool for it to take effect after changing your property editor.

This piece of code hooks into the start up handler and searches for any of the new tree pickers using the old data format. It then updates the values in the published document and the unpublished version (but not in the history) to have the new UDIs, and republishes the XML files.

Sometimes you may need to manually hit /umbraco/dialogs/republish.aspx?xml=true again

@jpwalters1
Copy link

jpwalters1 commented Jun 25, 2019

Wow exactly what I needed.

According to your main query, I had over 2,100 items that needed updating. I struggled a bit in getting this deployed! Not even sure if I used the right approach -- so I thought I'd write in case someone else needs help!

I downloaded the source 7.14.0, added a Class Library to the main solution called "sniper.umbraco". Added the two .cs files plus the references Umbraco.Core, Umbraco.Web, etc. and was able to compile a .dll. Put the .dll into the bin folder of the site, recycled the App Pool, and restarted the application for good measure. It restarted but didn't appear to do anything. I re-ran the main query that identifies these records and they are still untouched. I suspected the hook wasn't working.

Then I looked at StartupHandler and realized I needed to create a web.config appSettings key. Restarted one more time and it worked like a champ. I reran the query and saw 0 records.

core
libs
query
webconfig

Thanks,
Jason

@kiasyn
Copy link
Author

kiasyn commented Jun 27, 2019

@jpwalters1 cool that it worked for you.

We usually have a class library project that we add to our solutions called Sniper.Umbraco that has the UmbracoCms.Core nuget package installed. This project is added to all our sites and has helper umbraco utilities (like this one).

Once the migration is complete, you actually don't need it anymore and can disable / remove the code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment