Skip to content

Commit

Permalink
Merge branch 'master' into fix/remove-old-docs-and-projects
Browse files Browse the repository at this point in the history
  • Loading branch information
seangwright committed Nov 15, 2024
2 parents 737b82c + 1b23515 commit a2c6606
Show file tree
Hide file tree
Showing 29 changed files with 435 additions and 174 deletions.
2 changes: 1 addition & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Auto detect text files and perform LF normalization
* text=auto
* text=CRLF

*.cs text=CRLF diff=csharp
*.html text diff=html
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,27 +103,30 @@ public async Task<CommandResult> Handle(MigratePageTypesCommand request, Cancell
bool hasFieldsAlready = true;
foreach (var cmml in classMapping.Mappings.Where(m => m.IsTemplate).ToLookup(x => x.SourceFieldName))
{
var cmm = cmml.FirstOrDefault() ?? throw new InvalidOperationException();
if (fieldInReusableSchemas.ContainsKey(cmm.TargetFieldName))
foreach (var cmm in cmml)
{
// part of reusable schema
continue;
}
if (fieldInReusableSchemas.ContainsKey(cmm.TargetFieldName))
{
// part of reusable schema
continue;
}

var sc = cmsClasses.FirstOrDefault(sc => sc.ClassName.Equals(cmm.SourceClassName, StringComparison.InvariantCultureIgnoreCase))
?? throw new NullReferenceException($"The source class '{cmm.SourceClassName}' does not exist - wrong mapping {classMapping}");
var sc = cmsClasses.FirstOrDefault(sc => sc.ClassName.Equals(cmm.SourceClassName, StringComparison.InvariantCultureIgnoreCase))
?? throw new NullReferenceException($"The source class '{cmm.SourceClassName}' does not exist - wrong mapping {classMapping}");

var fi = new FormInfo(sc.ClassFormDefinition);
if (nfi.GetFormField(cmm.TargetFieldName) is { })
{
}
else
{
var src = fi.GetFormField(cmm.SourceFieldName);
src.Name = cmm.TargetFieldName;
nfi.AddFormItem(src);
hasFieldsAlready = false;
var fi = new FormInfo(sc.ClassFormDefinition);
if (nfi.GetFormField(cmm.TargetFieldName) is { })
{
}
else
{
var src = fi.GetFormField(cmm.SourceFieldName);
src.Name = cmm.TargetFieldName;
nfi.AddFormItem(src);
hasFieldsAlready = false;
}
}
//var cmm = cmml.FirstOrDefault() ?? throw new InvalidOperationException();
}

if (!hasFieldsAlready)
Expand Down Expand Up @@ -151,6 +154,11 @@ public async Task<CommandResult> Handle(MigratePageTypesCommand request, Cancell

foreach (string sourceClassName in classMapping.SourceClassNames)
{
if (newDt.ClassContentTypeType is ClassContentTypeType.REUSABLE)
{
continue;
}

var sourceClass = cmsClasses.First(c => c.ClassName.Equals(sourceClassName, StringComparison.InvariantCultureIgnoreCase));
foreach (var cmsClassSite in modelFacade.SelectWhere<ICmsClassSite>("ClassId = @classId", new SqlParameter("classId", sourceClass.ClassID)))
{
Expand Down Expand Up @@ -237,26 +245,29 @@ public async Task<CommandResult> Handle(MigratePageTypesCommand request, Cancell
var kxoDataClass = kxpClassFacade.GetClass(ksClass.ClassGUID);
protocol.FetchedTarget(kxoDataClass);

if (SaveUsingKxoApi(ksClass, kxoDataClass) is { } targetClassId)
if (SaveUsingKxoApi(ksClass, kxoDataClass) is { } targetClass)
{
foreach (var cmsClassSite in modelFacade.SelectWhere<ICmsClassSite>("ClassID = @classId", new SqlParameter("classId", ksClass.ClassID)))
if (targetClass.ClassContentTypeType is ClassContentTypeType.WEBSITE)
{
if (modelFacade.SelectById<ICmsSite>(cmsClassSite.SiteID) is { SiteGUID: var siteGuid })
foreach (var cmsClassSite in modelFacade.SelectWhere<ICmsClassSite>("ClassID = @classId", new SqlParameter("classId", ksClass.ClassID)))
{
if (ChannelInfoProvider.ProviderObject.Get(siteGuid) is { ChannelID: var channelId })
if (modelFacade.SelectById<ICmsSite>(cmsClassSite.SiteID) is { SiteGUID: var siteGuid })
{
var info = new ContentTypeChannelInfo { ContentTypeChannelChannelID = channelId, ContentTypeChannelContentTypeID = targetClassId };
ContentTypeChannelInfoProvider.ProviderObject.Set(info);
if (ChannelInfoProvider.ProviderObject.Get(siteGuid) is { ChannelID: var channelId })
{
var info = new ContentTypeChannelInfo { ContentTypeChannelChannelID = channelId, ContentTypeChannelContentTypeID = targetClass.ClassID };
ContentTypeChannelInfoProvider.ProviderObject.Set(info);
}
else
{
logger.LogWarning("Channel for site with SiteGUID '{SiteGuid}' not found", siteGuid);
}
}
else
{
logger.LogWarning("Channel for site with SiteGUID '{SiteGuid}' not found", siteGuid);
logger.LogWarning("Source site with SiteID '{SiteId}' not found", cmsClassSite.SiteID);
}
}
else
{
logger.LogWarning("Source site with SiteID '{SiteId}' not found", cmsClassSite.SiteID);
}
}
}
}
Expand Down Expand Up @@ -332,7 +343,7 @@ private async Task MigratePageTemplateConfigurations()
}
}

private int? SaveUsingKxoApi(ICmsClass ksClass, DataClassInfo kxoDataClass)
private DataClassInfo? SaveUsingKxoApi(ICmsClass ksClass, DataClassInfo kxoDataClass)
{
var mapped = dataClassMapper.Map(ksClass, kxoDataClass);
protocol.MappedTarget(mapped);
Expand Down Expand Up @@ -360,7 +371,7 @@ private async Task MigratePageTemplateConfigurations()
dataClassInfo.ClassID
);

return dataClassInfo.ClassID;
return dataClassInfo;
}
}
catch (Exception ex)
Expand Down
18 changes: 6 additions & 12 deletions KVA/Migration.Tool.Source/Handlers/MigratePagesCommandHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Collections.Concurrent;
using System.Diagnostics;

using CMS.ContentEngine;
using CMS.ContentEngine.Internal;
using CMS.Core;
Expand All @@ -12,12 +11,9 @@
using CMS.Websites.Routing.Internal;
using Kentico.Xperience.UMT.Model;
using Kentico.Xperience.UMT.Services;

using MediatR;

using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Logging;

using Migration.Tool.Common;
using Migration.Tool.Common.Abstractions;
using Migration.Tool.Common.Helpers;
Expand All @@ -30,7 +26,6 @@
using Migration.Tool.Source.Model;
using Migration.Tool.Source.Providers;
using Migration.Tool.Source.Services;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

Expand Down Expand Up @@ -218,14 +213,13 @@ public async Task<CommandResult> Handle(MigratePagesCommand request, Cancellatio
var commonDataInfos = new List<ContentItemCommonDataInfo>();
foreach (var umtModel in results)
{
var result = await importer.ImportAsync(umtModel);
if (result is { Success: false })
{
logger.LogError("Failed to import: {Exception}, {ValidationResults}", result.Exception, JsonConvert.SerializeObject(result.ModelValidationResults));
}

switch (result)
switch (await importer.ImportAsync(umtModel))
{
case { Success: false } result:
{
logger.LogError("Failed to import: {Exception}, {ValidationResults}", result.Exception, JsonConvert.SerializeObject(result.ModelValidationResults));
break;
}
case { Success: true, Imported: ContentItemCommonDataInfo ccid }:
{
commonDataInfos.Add(ccid);
Expand Down
7 changes: 5 additions & 2 deletions KVA/Migration.Tool.Source/Mappers/CmsClassMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public class CmsClassMapper(
PrimaryKeyMappingContext primaryKeyMappingContext,
IProtocol protocol,
FieldMigrationService fieldMigrationService,
ModelFacade modelFacade
ModelFacade modelFacade,
ToolConfiguration configuration
)
: EntityMapperBase<ICmsClass, DataClassInfo>(logger,
primaryKeyMappingContext, protocol)
Expand Down Expand Up @@ -188,7 +189,9 @@ protected override DataClassInfo MapInternal(ICmsClass source, DataClassInfo tar
}:
{
target.ClassType = ClassType.CONTENT_TYPE;
target.ClassContentTypeType = ClassContentTypeType.WEBSITE;
target.ClassContentTypeType = configuration.ClassNamesConvertToContentHub.Contains(target.ClassName)
? ClassContentTypeType.REUSABLE
: ClassContentTypeType.WEBSITE;

target = PatchDataClassInfo(target, out string? oldPrimaryKeyName, out string? documentNameField);
break;
Expand Down
28 changes: 17 additions & 11 deletions KVA/Migration.Tool.Source/Mappers/ContentItemMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,22 @@ protected override IEnumerable<IUmtModel> MapInternal(CmsTreeMapperSource source
var sourceNodeClass = modelFacade.SelectById<ICmsClass>(cmsTree.NodeClassID) ?? throw new InvalidOperationException($"Fatal: node class is missing, class id '{cmsTree.NodeClassID}'");
var mapping = classMappingProvider.GetMapping(sourceNodeClass.ClassName);
var targetClassGuid = sourceNodeClass.ClassGUID;
DataClassInfo targetClassInfo = null;
if (mapping != null)
{
targetClassGuid = DataClassInfoProvider.ProviderObject.Get(mapping.TargetClassName)?.ClassGUID ?? throw new InvalidOperationException($"Unable to find target class '{mapping.TargetClassName}'");
targetClassInfo = DataClassInfoProvider.ProviderObject.Get(mapping.TargetClassName) ?? throw new InvalidOperationException($"Unable to find target class '{mapping.TargetClassName}'");
targetClassGuid = targetClassInfo.ClassGUID;
}

bool migratedAsContentFolder = sourceNodeClass.ClassName.Equals("cms.folder", StringComparison.InvariantCultureIgnoreCase) && !configuration.UseDeprecatedFolderPageType.GetValueOrDefault(false);

var contentItemGuid = spoiledGuidContext.EnsureNodeGuid(cmsTree.NodeGUID, cmsTree.NodeSiteID, cmsTree.NodeID);
bool isMappedTypeReusable = (targetClassInfo?.ClassContentTypeType is ClassContentTypeType.REUSABLE) || configuration.ClassNamesConvertToContentHub.Contains(sourceNodeClass.ClassName);
yield return new ContentItemModel
{
ContentItemGUID = contentItemGuid,
ContentItemName = safeNodeName,
ContentItemIsReusable = false, // page is not reusable
ContentItemIsReusable = isMappedTypeReusable,
ContentItemIsSecured = cmsTree.IsSecuredNode ?? false,
ContentItemDataClassGuid = migratedAsContentFolder ? null : targetClassGuid,
ContentItemChannelGuid = siteGuid
Expand Down Expand Up @@ -350,16 +353,19 @@ protected override IEnumerable<IUmtModel> MapInternal(CmsTreeMapperSource source
Debug.Assert(cmsTree.NodeLinkedNodeID == null, "cmsTree.NodeLinkedNodeId == null");
Debug.Assert(cmsTree.NodeLinkedNodeSiteID == null, "cmsTree.NodeLinkedNodeSiteId == null");

yield return new WebPageItemModel
if (!isMappedTypeReusable)
{
WebPageItemParentGuid = nodeParentGuid, // NULL => under root
WebPageItemGUID = contentItemGuid,
WebPageItemName = safeNodeName,
WebPageItemTreePath = treePath,
WebPageItemWebsiteChannelGuid = siteGuid,
WebPageItemContentItemGuid = contentItemGuid,
WebPageItemOrder = cmsTree.NodeOrder ?? 0 // 0 is nullish value
};
yield return new WebPageItemModel
{
WebPageItemParentGuid = nodeParentGuid, // NULL => under root
WebPageItemGUID = contentItemGuid,
WebPageItemName = safeNodeName,
WebPageItemTreePath = treePath,
WebPageItemWebsiteChannelGuid = siteGuid,
WebPageItemContentItemGuid = contentItemGuid,
WebPageItemOrder = cmsTree.NodeOrder ?? 0 // 0 is nullish value
};
}
}

private IEnumerable<IUmtModel> MigrateDraft(ICmsVersionHistory checkoutVersion, ICmsTree cmsTree, string sourceFormClassDefinition, string targetFormDefinition, Guid contentItemGuid,
Expand Down
5 changes: 3 additions & 2 deletions KVA/Migration.Tool.Source/Mappers/MediaFileInfoMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ ToolConfiguration toolConfiguration
return new MediaFileInfo();
}

protected override MediaFileInfo MapInternal(MediaFileInfoMapperSource args, MediaFileInfo target, bool newInstance, MappingHelper mappingHelper, AddFailure addFailure)
protected override MediaFileInfo MapInternal(MediaFileInfoMapperSource source, MediaFileInfo target, bool newInstance, MappingHelper mappingHelper, AddFailure addFailure)
{
(string fullMediaFilePath, var mediaFile, int targetLibraryId, var file, _, bool migrateOnlyMediaFileInfo, var safeMediaFileGuid) = args;
(string fullMediaFilePath, var mediaFile, int targetLibraryId, var file, _, bool migrateOnlyMediaFileInfo, var safeMediaFileGuid) = source;

target.FileName = mediaFile.FileName;
target.FileTitle = mediaFile.FileTitle;
Expand Down Expand Up @@ -100,6 +100,7 @@ protected override MediaFileInfo MapInternal(MediaFileInfoMapperSource args, Med
addFailure(HandbookReferences.MediaFileIsMissingOnSourceFilesystem
.WithId(nameof(mediaFile.FileID), mediaFile.FileID)
.WithData(new { mediaFile.FilePath, mediaFile.FileGUID, mediaFile.FileLibraryID, mediaFile.FileSiteID, SearchedPath = fullMediaFilePath })
.WithSuggestion($"If you have a backup, copy it to the filesystem on path {source.FullMediaFilePath}. Otherwise, delete the media file from the source instance using admin web interface.")
.AsFailure<MediaFileInfo>()
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,12 @@ public async Task<IMigrateAttachmentResult> MigrateAttachment(ICmsAttachment ksA

if (ksAttachment.AttachmentBinary is null)
{
logger.LogError("Binary data is null, cannot migrate attachment: {Attachment}", ksAttachment);
logger.LogError("Attachment binary data is null {Attachment} " +
"Option 1: Via admin web interface of your source instance navigate to the attachment and update the data. " +
"Option 2: Update the database directly - table CMS_Attachment, column AttachmentBinary. " +
"Option 3: Via admin web interface of your source instance remove all attachment references, then remove the attachment",
new { ksAttachment.AttachmentName, ksAttachment.AttachmentSiteID, ksAttachment.AttachmentID });

throw new InvalidOperationException("Attachment data is null!");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,12 @@ public async Task<IMigrateAttachmentResult> MigrateAttachment(ICmsAttachment ksA
return DummyUploadedFile.FromStream(ms, attachment.AttachmentMimeType, attachment.AttachmentSize, attachment.AttachmentName);
}

logger.LogWarning("Attachment binary is null! {Attachment}", new { attachment.AttachmentName, attachment.AttachmentSiteID, attachment.AttachmentID });
logger.LogError("Attachment binary data is null {Attachment} " +
"Option 1: Via admin web interface of your source instance navigate to the attachment and update the data. " +
"Option 2: Update the database directly - table CMS_Attachment, column AttachmentBinary. " +
"Option 3: Via admin web interface of your source instance remove all attachment references, then remove the attachment",
new { attachment.AttachmentName, attachment.AttachmentSiteID, attachment.AttachmentID });

return null;
}

Expand Down
Loading

0 comments on commit a2c6606

Please sign in to comment.