Skip to content

Commit ee4a74a

Browse files
authored
Merge pull request #313 from Kentico/rls/v1.6.0
Rls/v1.6.0
2 parents 0e949b3 + e57fb23 commit ee4a74a

File tree

63 files changed

+2134
-1556
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+2134
-1556
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
---
22
name: Bug report
33
about: Create a report to help us improve
4-
54
---
65

76
### Brief bug description
87

98
What went wrong?
109

10+
## Output logs
11+
12+
Please include the command line output log file and migration protocol generated for your `Migration.Tool.CLI.exe migrate` command.
13+
1114
### Repro steps
1215

1316
1. Go to '...'
@@ -21,9 +24,9 @@ What is the correct behavior?
2124

2225
### Test environment
2326

24-
- Platform/OS: [e.g. .NET Core 2.1, iOS]
25-
- Browser [e.g. chrome, safari]
26-
- Version [e.g. 22]
27+
- Platform/OS: [e.g. .NET Core 2.1, iOS]
28+
- Browser [e.g. chrome, safari]
29+
- Version [e.g. 22]
2730

2831
### Additional context
2932

KVA/Migration.Tool.Source/Behaviors/XbKApiContextBehavior.cs renamed to KVA/Migration.Tool.Source/Behaviors/XbyKApiContextBehavior.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111

1212
namespace Migration.Tool.Source.Behaviors;
1313

14-
public class XbKApiContextBehavior<TRequest, TResponse>(
15-
ILogger<XbKApiContextBehavior<TRequest, TResponse>> logger,
14+
public class XbyKApiContextBehavior<TRequest, TResponse>(
15+
ILogger<XbyKApiContextBehavior<TRequest, TResponse>> logger,
1616
IMigrationProtocol protocol,
1717
KxpApiInitializer initializer)
1818
: IPipelineBehavior<TRequest, TResponse>
@@ -28,9 +28,9 @@ public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TRe
2828
{
2929
protocol.Append(HandbookReferences
3030
.MissingRequiredDependency<UserInfo>()
31-
.WithMessage($"Target XbK doesn't contain default administrator account ('{UserInfoProvider.DEFAULT_ADMIN_USERNAME}'). Default administrator account is required for migration.")
31+
.WithMessage($"Target XbyK doesn't contain default administrator account ('{UserInfoProvider.DEFAULT_ADMIN_USERNAME}'). Default administrator account is required for migration.")
3232
);
33-
throw new InvalidOperationException($"Target XbK doesn't contain default administrator account ('{UserInfoProvider.DEFAULT_ADMIN_USERNAME}')");
33+
throw new InvalidOperationException($"Target XbyK doesn't contain default administrator account ('{UserInfoProvider.DEFAULT_ADMIN_USERNAME}')");
3434
}
3535

3636
using (new CMSActionContext(defaultAdmin) { User = defaultAdmin, UseGlobalAdminContext = true })

KVA/Migration.Tool.Source/Contexts/SourceObjectContext.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
namespace Migration.Tool.Source.Contexts;
66

77
public record DocumentSourceObjectContext(ICmsTree CmsTree, ICmsClass NodeClass, ICmsSite Site, FormInfo OldFormInfo, FormInfo NewFormInfo, int? DocumentId) : ISourceObjectContext;
8+
9+
public record CustomTableSourceObjectContext : ISourceObjectContext;

KVA/Migration.Tool.Source/Handlers/MigrateCategoriesCommandHandler.cs

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using System.Collections;
2-
2+
using System.Diagnostics;
33
using CMS.ContentEngine;
44
using CMS.ContentEngine.Internal;
55
using CMS.DataEngine;
@@ -12,12 +12,14 @@
1212

1313
using Microsoft.Data.SqlClient;
1414
using Microsoft.Extensions.Logging;
15-
1615
using Migration.Tool.Common;
1716
using Migration.Tool.Common.Abstractions;
17+
using Migration.Tool.Common.Builders;
1818
using Migration.Tool.Common.Helpers;
19+
using Migration.Tool.KXP.Api;
1920
using Migration.Tool.Source.Mappers;
2021
using Migration.Tool.Source.Model;
22+
using Migration.Tool.Source.Providers;
2123
using Migration.Tool.Source.Services;
2224

2325
using Newtonsoft.Json;
@@ -30,7 +32,9 @@ public class MigrateCategoriesCommandHandler(
3032
IImporter importer,
3133
ReusableSchemaService reusableSchemaService,
3234
IUmtMapper<TagModelSource> tagModelMapper,
33-
SpoiledGuidContext spoiledGuidContext
35+
SpoiledGuidContext spoiledGuidContext,
36+
KxpClassFacade kxpClassFacade,
37+
ClassMappingProvider classMappingProvider
3438
) : IRequestHandler<MigrateCategoriesCommand, CommandResult>
3539
{
3640
public async Task<CommandResult> Handle(MigrateCategoriesCommand request, CancellationToken cancellationToken)
@@ -48,26 +52,42 @@ public async Task<CommandResult> Handle(MigrateCategoriesCommand request, Cancel
4852
if (result.Imported is TaxonomyInfo taxonomy)
4953
{
5054
string query = """
51-
SELECT C.ClassName, C.ClassGuid, C.ClassID
55+
SELECT C.ClassName, C.ClassGuid, C.ClassID, CC.CategoryID
5256
FROM View_CMS_Tree_Joined [TJ]
5357
JOIN dbo.CMS_DocumentCategory [CDC] on [TJ].DocumentID = [CDC].DocumentID
5458
JOIN CMS_Class [C] ON TJ.NodeClassID = [C].ClassID
5559
JOIN dbo.CMS_Category CC on CDC.CategoryID = CC.CategoryID AND CC.CategoryUserID IS NULL
56-
GROUP BY C.ClassName, C.ClassGuid, C.ClassID
60+
GROUP BY C.ClassName, C.ClassGuid, C.ClassID, CC.CategoryID
5761
""";
5862

59-
var classesWithCategories = modelFacade.Select(query, (reader, version) => new { ClassName = reader.Unbox<string>("ClassName"), ClassGuid = reader.Unbox<Guid>("ClassGuid"), ClassID = reader.Unbox<int>("ClassID") });
63+
var classesWithCategories = modelFacade.Select(query, (reader, version) => new { ClassName = reader.Unbox<string>("ClassName"), ClassGuid = reader.Unbox<Guid>("ClassGuid"), ClassID = reader.Unbox<int>("ClassID"), CategoryID = reader.Unbox<int>("CategoryID") })
64+
.GroupBy(x => x.ClassGuid)
65+
.Select(x => new { ClassGuid = x.Key, x.First().ClassName, x.First().ClassID, Categories = x.Select(row => row.CategoryID) });
6066

67+
// For each source instance class whose documents have some categories assigned, include taxonomy-storage reusable schema in the target class
68+
#region Ensure reusable schema
6169
var skippedClasses = new List<int>();
6270
var schemaGuid = Guid.Empty;
6371
string categoryFieldName = "Category_Legacy";
6472
foreach (var classWithCategoryUsage in classesWithCategories)
6573
{
6674
var targetDataClass = DataClassInfoProvider.ProviderObject.Get(classWithCategoryUsage.ClassGuid);
67-
if (targetDataClass == null)
75+
if (targetDataClass is null)
6876
{
69-
skippedClasses.Add(classWithCategoryUsage.ClassID);
70-
logger.LogWarning("Data class not found by ClassGuid {Guid}", classWithCategoryUsage.ClassGuid);
77+
// No direct-mapped target class found. Try to identify custom-mapped target class
78+
var classMapping = classMappingProvider.GetMapping(classWithCategoryUsage.ClassName);
79+
if (classMapping is not null)
80+
{
81+
if (classWithCategoryUsage.Categories.Any(cat => classMapping.IsCategoryMapped(classWithCategoryUsage.ClassName, cat)))
82+
{
83+
targetDataClass = kxpClassFacade.GetClass(classMapping.TargetClassName);
84+
}
85+
}
86+
}
87+
88+
if (targetDataClass is null)
89+
{
90+
logger.LogWarning($"Class(ClassGuid {{Guid}}) has documents with categories, but no directly-mapped data class nor custom-mapped class that would receive the categories (declared via {nameof(IClassMapping.IsCategoryMapped)}) was found", classWithCategoryUsage.ClassGuid);
7191
continue;
7292
}
7393

@@ -82,6 +102,8 @@ FROM View_CMS_Tree_Joined [TJ]
82102
DataClassInfoProvider.SetDataClassInfo(targetDataClass);
83103
}
84104
}
105+
#endregion
106+
85107

86108
var categories = modelFacade.Select<ICmsCategory>(
87109
"CategoryEnabled = 1 AND CategoryUserID IS NULL",
@@ -95,23 +117,24 @@ FROM View_CMS_Tree_Joined [TJ]
95117
categoryId2Guid.Add(cmsCategory.CategoryID, cmsCategory.CategoryGUID);
96118
// CategorySiteID - not migrated, Taxonomies are global!
97119

98-
var mapped = tagModelMapper.Map(new TagModelSource(
120+
var tagUMTModels = tagModelMapper.Map(new TagModelSource(
99121
taxonomy.TaxonomyGUID,
100122
cmsCategory,
101123
categoryId2Guid
102124
));
103125

104-
foreach (var umtModel in mapped)
126+
foreach (var tagUMTModel in tagUMTModels)
105127
{
106128
if (await importer
107-
.ImportAsync(umtModel)
129+
.ImportAsync(tagUMTModel)
108130
.AssertSuccess<TagInfo>(logger) is { Success: true, Info: { } tag })
109131
{
110132
query = """
111-
SELECT TJ.DocumentGUID, TJ.NodeSiteID, TJ.NodeID, TJ.DocumentID, CDC.CategoryID, TJ.DocumentCheckedOutVersionHistoryID, TJ.NodeClassID
133+
SELECT TJ.DocumentGUID, TJ.NodeSiteID, TJ.NodeID, TJ.DocumentID, CDC.CategoryID, TJ.DocumentCheckedOutVersionHistoryID, TJ.NodeClassID, C.ClassName
112134
FROM View_CMS_Tree_Joined [TJ]
113135
JOIN dbo.CMS_DocumentCategory [CDC] on [TJ].DocumentID = [CDC].DocumentID
114136
JOIN dbo.CMS_Category CC on CDC.CategoryID = CC.CategoryID AND CC.CategoryUserID IS NULL
137+
JOIN CMS_Class [C] ON TJ.NodeClassID = [C].ClassID
115138
WHERE CDC.CategoryID = @categoryId
116139
""";
117140

@@ -120,6 +143,7 @@ FROM View_CMS_Tree_Joined [TJ]
120143
CategoryID = reader.Unbox<int?>("CategoryID"),
121144
DocumentCheckedOutVersionHistoryID = reader.Unbox<int?>("DocumentCheckedOutVersionHistoryID"),
122145
NodeClassID = reader.Unbox<int>("NodeClassID"),
146+
NodeClassName = reader.Unbox<string>("ClassName"),
123147
NodeSiteID = reader.Unbox<int>("NodeSiteID"),
124148
DocumentGUID = spoiledGuidContext.EnsureDocumentGuid(
125149
reader.Unbox<Guid>("DocumentGUID"),
@@ -137,13 +161,23 @@ FROM View_CMS_Tree_Joined [TJ]
137161
continue;
138162
}
139163

164+
var classMapping = classMappingProvider.GetMapping(dwc.NodeClassName);
165+
if (classMapping is not null)
166+
{
167+
Debug.Assert(dwc.CategoryID.HasValue, "dwc.CategoryID should have value, otherwise the row would not be included in the query due to inner join");
168+
if (!classMapping.IsCategoryMapped(dwc.NodeClassName, dwc.CategoryID.Value))
169+
{
170+
continue;
171+
}
172+
}
173+
140174
var commonData = ContentItemCommonDataInfo.Provider.Get()
141175
.WhereEquals(nameof(ContentItemCommonDataInfo.ContentItemCommonDataGUID), dwc.DocumentGUID)
142176
.FirstOrDefault();
143177

144178
if (commonData is null)
145179
{
146-
logger.LogWarning("ContentItemCommonDataInfo not found by guid {Guid}, taxonomy cannot be migrated", dwc.DocumentGUID);
180+
logger.LogWarning("ContentItemCommonDataInfo not found by Guid {Guid}. Taxonomy cannot be migrated", dwc.DocumentGUID);
147181
continue;
148182
}
149183

KVA/Migration.Tool.Source/Handlers/MigrateCustomModulesCommandHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ private async Task MigrateClasses(EntityConfiguration entityConfiguration, Cance
173173
}
174174
}
175175

176-
// special case - member migration (CMS_User splits into CMS_User and CMS_Member in XbK)
176+
// special case - member migration (CMS_User splits into CMS_User and CMS_Member in XbyK)
177177
await MigrateMemberClass(cancellationToken);
178178
}
179179

0 commit comments

Comments
 (0)