Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

non-unique DocumentGUID & NodeGUID support #203

Merged
merged 3 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions KVA/Migration.Toolkit.Source/Auxiliary/NodeXmlAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public bool HasValueSet(string columnName)
public int? NodeParentID => _xClass.Element(NodeXmlColumns.NODE_PARENT_ID)?.Value<int>();
public int? NodeLevel => _xClass.Element(NodeXmlColumns.NODE_LEVEL)?.Value<int>();
public int? NodeSiteID => _xClass.Element(NodeXmlColumns.NODE_SITE_ID)?.Value<int>();
[Obsolete("NodeGUID is not unique, use other means of node identification", true)]
public Guid? NodeGUID => _xClass.Element(NodeXmlColumns.NODE_GUID)?.Value<Guid>();
public int? NodeOrder => _xClass.Element(NodeXmlColumns.NODE_ORDER)?.Value<int>();
public int? NodeOwner => _xClass.Element(NodeXmlColumns.NODE_OWNER)?.Value<int>();
Expand All @@ -63,6 +64,7 @@ public bool HasValueSet(string columnName)
public string? DocumentContent => _xClass.Element(NodeXmlColumns.DOCUMENT_CONTENT)?.Value;
public string? DocumentLastVersionNumber => _xClass.Element(NodeXmlColumns.DOCUMENT_LAST_VERSION_NUMBER)?.Value;
public bool? DocumentIsArchived => _xClass.Element(NodeXmlColumns.DOCUMENT_IS_ARCHIVED)?.ValueAsBool();
[Obsolete("DocumentGUID is not unique, use other means of document identification", true)]
public Guid? DocumentGUID => _xClass.Element(NodeXmlColumns.DOCUMENT_GUID)?.Value<Guid>();
public Guid? DocumentWorkflowCycleGUID => _xClass.Element(NodeXmlColumns.DOCUMENT_WORKFLOW_CYCLE_GUID)?.Value<Guid>();
public bool? DocumentCanBePublished => _xClass.Element(NodeXmlColumns.DOCUMENT_CAN_BE_PUBLISHED)?.ValueAsBool();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public class MigrateCategoriesCommandHandler(
ModelFacade modelFacade,
IImporter importer,
ReusableSchemaService reusableSchemaService,
IUmtMapper<TagModelSource> tagModelMapper
IUmtMapper<TagModelSource> tagModelMapper,
SpoiledGuidContext spoiledGuidContext
) : IRequestHandler<MigrateCategoriesCommand, CommandResult>
{
public async Task<CommandResult> Handle(MigrateCategoriesCommand request, CancellationToken cancellationToken)
Expand Down Expand Up @@ -106,7 +107,7 @@ FROM View_CMS_Tree_Joined [TJ]
.AssertSuccess<TagInfo>(logger) is {Success:true, Info: {} tag})
{
query = """
SELECT TJ.DocumentGUID, CDC.CategoryID, TJ.DocumentCheckedOutVersionHistoryID, TJ.NodeClassID
SELECT TJ.DocumentGUID, TJ.NodeSiteID, TJ.NodeID, TJ.DocumentID, CDC.CategoryID, TJ.DocumentCheckedOutVersionHistoryID, TJ.NodeClassID
FROM View_CMS_Tree_Joined [TJ]
JOIN dbo.CMS_DocumentCategory [CDC] on [TJ].DocumentID = [CDC].DocumentID
JOIN dbo.CMS_Category CC on CDC.CategoryID = CC.CategoryID AND CC.CategoryUserID IS NULL
Expand All @@ -115,10 +116,16 @@ FROM View_CMS_Tree_Joined [TJ]

var docsWithCategories = modelFacade.Select(query, (reader, _) => new
{
DocumentGUID = reader.Unbox<Guid>("DocumentGUID"),
CategoryID = reader.Unbox<int?>("CategoryID"),
DocumentCheckedOutVersionHistoryID = reader.Unbox<int?>("DocumentCheckedOutVersionHistoryID"),
NodeClassID = reader.Unbox<int>("NodeClassID")
NodeClassID = reader.Unbox<int>("NodeClassID"),
NodeSiteID = reader.Unbox<int>("NodeSiteID"),
DocumentGUID = spoiledGuidContext.EnsureDocumentGuid(
reader.Unbox<Guid>("DocumentGUID"),
reader.Unbox<int>("NodeSiteID"),
reader.Unbox<int>("NodeID"),
reader.Unbox<int>("DocumentID")
)
}, new SqlParameter("categoryId", cmsCategory.CategoryID));

foreach (var dwc in docsWithCategories)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public class MigratePagesCommandHandler(
IImporter importer,
IUmtMapper<CmsTreeMapperSource> mapper,
ModelFacade modelFacade,
DeferredPathService deferredPathService
DeferredPathService deferredPathService,
SpoiledGuidContext spoiledGuidContext
)
: IRequestHandler<MigratePagesCommand, CommandResult>
{
Expand Down Expand Up @@ -109,7 +110,8 @@ public async Task<CommandResult> Handle(MigratePagesCommand request, Cancellatio
{
var linkedDocument = linkedNodeDocuments[i];
var fixedDocumentGuid = GuidHelper.CreateDocumentGuid($"{linkedDocument.DocumentID}|{ksNode.NodeID}|{ksNode.NodeSiteID}"); //Guid.NewGuid();
if (ContentItemInfo.Provider.Get(ksNode.NodeGUID)?.ContentItemID is { } contentItemId)
var patchedNodeGuid = spoiledGuidContext.EnsureNodeGuid(ksNode.NodeGUID, ksNode.NodeSiteID, ksNode.NodeID);
if (ContentItemInfo.Provider.Get(patchedNodeGuid)?.ContentItemID is { } contentItemId)
{
if (cultureCodeToLanguageGuid.TryGetValue(linkedDocument.DocumentCulture, out var languageGuid) &&
ContentLanguageInfoProvider.ProviderObject.Get(languageGuid) is { } languageInfo)
Expand Down Expand Up @@ -177,7 +179,7 @@ public async Task<CommandResult> Handle(MigratePagesCommand request, Cancellatio
var ksNodeParent = modelFacade.SelectById<ICmsTree>(ksNode.NodeParentID);
var nodeParentGuid = ksNodeParent?.NodeAliasPath == "/" || ksNodeParent == null
? (Guid?)null
: ksNodeParent?.NodeGUID;
: spoiledGuidContext.EnsureNodeGuid(ksNodeParent);

var targetClass = DataClassInfoProvider.ProviderObject.Get(ksNodeClass.ClassGUID);

Expand Down Expand Up @@ -415,7 +417,7 @@ private async Task MigratePageUrlPaths(Guid webPageItemGuid, int webPageItemId,
VersionStatus.InitialDraft => false,
VersionStatus.Draft => true,
VersionStatus.Published => false,
VersionStatus.Archived => false,
VersionStatus.Unpublished => false,
_ => throw new ArgumentOutOfRangeException()
},
};
Expand Down
2 changes: 2 additions & 0 deletions KVA/Migration.Toolkit.Source/KsCoreDiExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public static IServiceCollection UseKsToolkitCore(this IServiceCollection servic
services.AddTransient<IModuleLoader, ModuleLoader>();

services.AddSingleton<ModelFacade>();
services.AddSingleton<ISpoiledGuidContext, SpoiledGuidContext>();
services.AddSingleton<SpoiledGuidContext>(s => s.GetRequiredService<ISpoiledGuidContext>() as SpoiledGuidContext ?? throw new InvalidOperationException());

services.AddTransient<BulkDataCopyService>();
services.AddTransient<CmsRelationshipService>();
Expand Down
47 changes: 36 additions & 11 deletions KVA/Migration.Toolkit.Source/Mappers/ContentItemMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Migration.Toolkit.Source.Mappers;
using CMS.Websites;
using Kentico.Components.Web.Mvc.FormComponents;
using Kentico.Xperience.UMT.Model;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Logging;
using Migration.Toolkit.Common;
using Migration.Toolkit.Common.Abstractions;
Expand Down Expand Up @@ -49,7 +50,8 @@ public class ContentItemMapper(
KxpMediaFileFacade mediaFileFacade,
ModelFacade modelFacade,
ReusableSchemaService reusableSchemaService,
DeferredPathService deferredPathService
DeferredPathService deferredPathService,
SpoiledGuidContext spoiledGuidContext
) : UmtMapperBase<CmsTreeMapperSource>
{
private const string CLASS_FIELD_CONTROL_NAME = "controlname";
Expand All @@ -64,7 +66,7 @@ protected override IEnumerable<IUmtModel> MapInternal(CmsTreeMapperSource source
throw new InvalidOperationException($"Fatal: node class is missing, class id '{cmsTree.NodeClassID}'");
}

var contentItemGuid = cmsTree.NodeGUID;
var contentItemGuid = spoiledGuidContext.EnsureNodeGuid(cmsTree.NodeGUID, cmsTree.NodeSiteID, cmsTree.NodeID);
yield return new ContentItemModel
{
ContentItemGUID = contentItemGuid,
Expand Down Expand Up @@ -154,9 +156,16 @@ protected override IEnumerable<IUmtModel> MapInternal(CmsTreeMapperSource source

PatchJsonDefinitions(source.CmsTree.NodeSiteID, ref contentItemCommonDataPageTemplateConfiguration, ref contentItemCommonDataPageBuilderWidgets, out var ndp);

var documentGuid = spoiledGuidContext.EnsureDocumentGuid(
cmsDocument.DocumentGUID ?? throw new InvalidOperationException($"DocumentGUID is null"),
cmsTree.NodeSiteID,
cmsTree.NodeID,
cmsDocument.DocumentID
);

var commonDataModel = new ContentItemCommonDataModel
{
ContentItemCommonDataGUID = cmsDocument.DocumentGUID ?? throw new InvalidOperationException($"DocumentGUID is null"),
ContentItemCommonDataGUID = documentGuid,
ContentItemCommonDataContentItemGuid = contentItemGuid,
ContentItemCommonDataContentLanguageGuid = languageGuid, // DocumentCulture -> language entity needs to be created and its ID used here
ContentItemCommonDataVersionStatus = versionStatus,
Expand Down Expand Up @@ -260,7 +269,7 @@ protected override IEnumerable<IUmtModel> MapInternal(CmsTreeMapperSource source

var languageMetadataInfo = new ContentItemLanguageMetadataModel
{
ContentItemLanguageMetadataGUID = cmsDocument.DocumentGUID,
ContentItemLanguageMetadataGUID = documentGuid,
ContentItemLanguageMetadataContentItemGuid = contentItemGuid,
ContentItemLanguageMetadataDisplayName = cmsDocument.DocumentName, // For the admin UI only
ContentItemLanguageMetadataLatestVersionStatus = draftMigrated ? VersionStatus.Draft : versionStatus, // That's the latest status of th item for admin optimization
Expand Down Expand Up @@ -308,7 +317,7 @@ private void PatchJsonDefinitions(int sourceSiteId, ref string? pageTemplateConf
sourceInstanceContext.GetPageTemplateFormComponents(sourceSiteId, pageTemplateConfigurationObj?.Identifier);
if (pageTemplateConfigurationObj.Properties is { Count: > 0 })
{
WalkProperties(pageTemplateConfigurationObj.Properties, pageTemplateConfigurationFcs, out var ndp);
WalkProperties(sourceSiteId, pageTemplateConfigurationObj.Properties, pageTemplateConfigurationFcs, out var ndp);
needsDeferredPatch = ndp || needsDeferredPatch;
}

Expand Down Expand Up @@ -466,6 +475,8 @@ private IEnumerable<IUmtModel> MigrateDraft(ICmsVersionHistory checkoutVersion,

#region "Page template & page widget walkers"

private record WalkerContext(int SiteId);

private void WalkAreas(int siteId, List<EditableAreaConfiguration> areas, out bool needsDeferredPatch)
{
needsDeferredPatch = false;
Expand All @@ -490,7 +501,7 @@ private void WalkSections(int siteId, List<SectionConfiguration> sections, out b

// TODO tk: 2022-09-14 find other acronym for FormComponents
var sectionFcs = sourceInstanceContext.GetSectionFormComponents(siteId, section.TypeIdentifier);
WalkProperties(section.Properties, sectionFcs, out var ndp1);
WalkProperties(siteId, section.Properties, sectionFcs, out var ndp1);
needsDeferredPatch = ndp1 || needsDeferredPatch;

if (section.Zones is { Count: > 0 })
Expand Down Expand Up @@ -530,14 +541,14 @@ private void WalkWidgets(int siteId, List<WidgetConfiguration> widgets, out bool

if (variant.Properties is { Count: > 0 })
{
WalkProperties(variant.Properties, widgetFcs, out var ndp);
WalkProperties(siteId, variant.Properties, widgetFcs, out var ndp);
needsDeferredPatch = ndp || needsDeferredPatch;
}
}
}
}

private void WalkProperties(JObject properties, List<EditingFormControlModel>? formControlModels, out bool needsDeferredPatch)
private void WalkProperties(int siteId, JObject properties, List<EditingFormControlModel>? formControlModels, out bool needsDeferredPatch)
{
needsDeferredPatch = false;
foreach (var (key, value) in properties)
Expand Down Expand Up @@ -574,7 +585,10 @@ private void WalkProperties(JObject properties, List<EditingFormControlModel>? f
{
if (value?.ToObject<List<Services.Model.PageSelectorItem>>() is { Count: > 0 } items)
{
properties[key] = JToken.FromObject(items.Select(x => new WebPageRelatedItem { WebPageGuid = x.NodeGuid }).ToList());
properties[key] = JToken.FromObject(items.Select(x => new WebPageRelatedItem
{
WebPageGuid = spoiledGuidContext.EnsureNodeGuid(x.NodeGuid, siteId)
}).ToList());
}

logger.LogTrace("Value migrated from {Old} model to {New} model", oldFormComponent, newFormComponent);
Expand Down Expand Up @@ -787,7 +801,9 @@ ICmsClass nodeClass
{
// relation to other document
var convertedRelation = relationshipService.GetNodeRelationships(cmsTree.NodeID)
.Select(r => new WebPageRelatedItem { WebPageGuid = r.RightNode.NodeGUID });
.Select(r => new WebPageRelatedItem {
WebPageGuid = spoiledGuidContext.EnsureNodeGuid(r.RightNode.NodeGUID, r.RightNode.NodeSiteID, r.RightNode.NodeID)
});

target.SetValueAsJson(columnName, convertedRelation);
}
Expand All @@ -801,7 +817,16 @@ ICmsClass nodeClass
{
if (value is string pageReferenceJson)
{
target[columnName] = pageReferenceJson.Replace("\"NodeGuid\"", "\"WebPageGuid\"");
var parsed = JObject.Parse(pageReferenceJson);
foreach (var jToken in parsed.DescendantsAndSelf())
{
if (jToken.Path.EndsWith("NodeGUID", StringComparison.InvariantCultureIgnoreCase))
{
var patchedGuid = spoiledGuidContext.EnsureNodeGuid(jToken.Value<Guid>(), cmsTree.NodeSiteID);
jToken.Replace(JToken.FromObject(patchedGuid));
}
}
target[columnName] = parsed.ToString().Replace("\"NodeGuid\"", "\"WebPageGuid\"");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace Migration.Toolkit.Source.Mappers;
using Migration.Toolkit.KXP.Models;
using Migration.Toolkit.Source.Contexts;
using Migration.Toolkit.Source.Model;
using Migration.Toolkit.Source.Services;
using Migration.Toolkit.Source.Services.Model;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Expand All @@ -23,7 +24,8 @@ public class PageTemplateConfigurationMapper(
ILogger<PageTemplateConfigurationMapper> logger,
PrimaryKeyMappingContext pkContext,
IProtocol protocol,
SourceInstanceContext sourceInstanceContext)
SourceInstanceContext sourceInstanceContext,
SpoiledGuidContext spoiledGuidContext)
: EntityMapperBase<ICmsPageTemplateConfiguration, PageTemplateConfigurationInfo>(logger, pkContext, protocol)
{
protected override PageTemplateConfigurationInfo? CreateNewInstance(ICmsPageTemplateConfiguration source, MappingHelper mappingHelper, AddFailure addFailure)
Expand Down Expand Up @@ -64,7 +66,7 @@ protected override PageTemplateConfigurationInfo MapInternal(ICmsPageTemplateCon
sourceInstanceContext.GetPageTemplateFormComponents(source.PageTemplateConfigurationSiteID, pageTemplateConfiguration.Identifier);
if (pageTemplateConfiguration.Properties is { Count: > 0 })
{
WalkProperties(pageTemplateConfiguration.Properties, pageTemplateConfigurationFcs);
WalkProperties(source.PageTemplateConfigurationSiteID, pageTemplateConfiguration.Properties, pageTemplateConfigurationFcs);
}

target.PageTemplateConfigurationTemplate = JsonConvert.SerializeObject(pageTemplateConfiguration);
Expand Down Expand Up @@ -119,7 +121,7 @@ private void WalkSections(int siteId, List<Migration.Toolkit.Source.Services.Mod
logger.LogTrace("Walk section {TypeIdentifier}|{Identifier}", section.TypeIdentifier, section.Identifier);

var sectionFcs = sourceInstanceContext.GetSectionFormComponents(siteId, section.TypeIdentifier);
WalkProperties(section.Properties, sectionFcs);
WalkProperties(siteId, section.Properties, sectionFcs);

if (section.Zones is { Count: > 0 })
{
Expand Down Expand Up @@ -154,13 +156,13 @@ private void WalkWidgets(int siteId, List<Migration.Toolkit.Source.Services.Mode

if (variant.Properties is { Count: > 0 })
{
WalkProperties(variant.Properties, widgetFcs);
WalkProperties(siteId, variant.Properties, widgetFcs);
}
}
}
}

private void WalkProperties(JObject properties, List<EditingFormControlModel>? formControlModels)
private void WalkProperties(int siteId, JObject properties, List<EditingFormControlModel>? formControlModels)
{
foreach (var (key, value) in properties)
{
Expand Down Expand Up @@ -192,7 +194,10 @@ private void WalkProperties(JObject properties, List<EditingFormControlModel>? f
{
if (value?.ToObject<List<Migration.Toolkit.Source.Services.Model.PageSelectorItem>>() is { Count: > 0 } items)
{
properties[key] = JToken.FromObject(items.Select(x => new WebPageRelatedItem { WebPageGuid = x.NodeGuid }).ToList());
properties[key] = JToken.FromObject(items.Select(x => new WebPageRelatedItem
{
WebPageGuid = spoiledGuidContext.EnsureNodeGuid(x.NodeGuid, siteId)
}).ToList());
}

logger.LogTrace("Value migrated from {Old} model to {New} model", oldFormComponent, newFormComponent);
Expand All @@ -207,8 +212,6 @@ private void WalkProperties(JObject properties, List<EditingFormControlModel>? f
}
else if (FieldMappingInstance.BuiltInModel.SupportedInKxpLegacyMode.Contains(editingFcm.FormComponentIdentifier))
{
// TODO tomas.krch 2024-03-27: nothing is supported in legacy mode (no legacy mode)

// OK
logger.LogTrace("Editing form component found {FormComponentName} => supported in legacy mode",
editingFcm.FormComponentIdentifier);
Expand Down
Loading