Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,18 @@ private Dictionary<string, IClassMapping> MappingsByClassName
return MappingsByClassName.GetValueOrDefault(className);
}

private Dictionary<string, (DataClassInfo target, IClassMapping mappping)>? manualMappings = null;
public Dictionary<string, (DataClassInfo target, IClassMapping mappping)> ExecuteMappings()
{
if (manualMappings is not null)
{
return manualMappings;
}

EnsureSettings();
ExecReusableSchemaBuilders();

var manualMappings = new Dictionary<string, (DataClassInfo target, IClassMapping mappping)>();
manualMappings = new Dictionary<string, (DataClassInfo target, IClassMapping mappping)>();
var metadataFields = GetLegacyMetadataFields(modelFacade.SelectVersion(), IncludedMetadata.Extended).ToArray();

foreach (var classMapping in MappingsByClassName.Values)
Expand Down
6 changes: 1 addition & 5 deletions KVA/Migration.Tool.Source/Services/MediaFileMigrator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,7 @@ private void RequireMigratedMediaFiles(List<(IMediaLibrary sourceLibrary, ICmsSi

try
{
if (newInstance)
{
mediaFileFacade.EnsureMediaFilePathExistsInLibrary(mf, targetMediaLibrary.LibraryID);
}

mediaFileFacade.EnsureMediaFilePathExistsInLibrary(mf, targetMediaLibrary.LibraryID);
mediaFileFacade.SetMediaFile(mf, newInstance);

protocol.Success(ksMediaFile, mf, mapped);
Expand Down
5 changes: 5 additions & 0 deletions Migration.Tool.CLI/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,12 +280,16 @@ Custom table migration does NOT include:

Media library files are migrated as [content item assets](https://docs.kentico.com/x/content_item_assets_xp) to Content hub into a content folder `<site_name>/<library_folder>`. All assets are created in the default language of the respective site. Migrated assets are created as content items of a _Legacy media file_ content type (code name `Legacy.Mediafile`) created by the tool.

If you want to use Xperience by Kentico's [automatic image optimization](https://docs.kentico.com/documentation/developers-and-admins/development/content-types#configure-asset-content-types) feature for the _Legacy media file_ type, follow along with our [guide about customizing asset migration](https://docs.kentico.com/x/optimize_images_during_upgrade_guides).

If required, you can [configure the tool](#convert-attachments-and-media-library-files-to-media-libraries-instead-of-content-item-assets) to instead migrate media libraries as media libraries on the target instance.

#### Attachments

Attachment files are migrated as [content item assets](https://docs.kentico.com/x/content_item_assets_xp) to Content hub into a content folder `<site_name>/__Attachments`. Assets are created in the specified language if the language is available (e.g., attachments of pages). Migrated assets are created as content items of a _Legacy attachment_ content type (code name `Legacy.Attachment`) created by the tool.

If you want to use Xperience by Kentico's [automatic image optimization](https://docs.kentico.com/documentation/developers-and-admins/development/content-types#configure-asset-content-types) feature for the _Legacy attachment_ type, follow along with our [guide about customizing asset migration](https://docs.kentico.com/x/optimize_images_during_upgrade_guides).

If required, you can [configure the tool](#convert-attachments-and-media-library-files-to-media-libraries-instead-of-content-item-assets) to instead migrate attachments as media libraries on the target instance.

#### Forms
Expand Down Expand Up @@ -426,6 +430,7 @@ Add the options under the `Settings` section in the configuration file.
| MigrateOnlyMediaFileInfo | If set to `true`, only the database representations of media files are migrated, without the files in the media folder in the project's file system. For example, enable this option if your media library files are mapped to a shared directory or Cloud storage.<br /><br />If `false`, media files are migrated based on the `KxCmsDirPath` location. |
| MigrateMediaToMediaLibrary | Determines whether media library files and attachments from the source instance are migrated to the target instance as media libraries or as [content item assets](https://docs.kentico.com/x/content_item_assets_xp) in the content hub. The default value is `false` – media files and attachments are migrated as content item assets. <br /><br /> See [Convert attachments and media library files to media libraries instead of content item assets](#convert-attachments-and-media-library-files-to-media-libraries-instead-of-content-item-assets) |
| LegacyFlatAssetTree | Use legacy behavior of versions up to 2.3.0. Content folders for asset content items will be created in a flat structure (all under root folder) |
| LegacyPermissiveMediaLibrarySubfolders | XbyK has restrictions on media library subfolder name. It must contain only alphanumeric characters, underscores ('_'), hyphens ('-'), and cannot contain file names reserved by the operating system (e.g., ‘CON’, ‘PRN’, ‘AUX’ for Windows). If set to `true`, name checks will be bypassed. Note that this may impair some XbyK functionality like migration of media libraries to content hub. User may prefer using this option e.g. for repeated migration after initially migrating with previous versions of Migration Tool, and then manually changing the names in XbyK | |
| AssetRootFolders | Dictionary defining the root folder for Asset content items per site: Key is site name (CMS_Site.SiteName). Value is in format _/FolderDisplayName1/FolderDisplayName2/..._ |
| TargetWorkspaceName | The code name of the [workspace](https://docs.kentico.com/x/workspaces_xp) to which content items, content folders and other related entities are migrated. This workspace is used if another workspace is not specified explicitly, for example by the content item director API in [migration customizations](../Migration.Tool.Extensions/README.md). This configuration is not necessary if the target project only contains a single workspace. |
| MemberIncludeUserSystemFields | Determines which system fields from the _CMS_User_ and _CMS_UserSettings_ tables are migrated to _CMS_Member_ in Xperience by Kentico. Fields that do not exist in _CMS_Member_ are automatically created. <br /><br />The sample `appsettings.json` file included with the tool by default includes all user fields that can be migrated from Kentico Xperience 13. Exclude specific fields from the migration by removing them from this configuration option. |
Expand Down
1 change: 1 addition & 0 deletions Migration.Tool.CLI/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"MigrateOnlyMediaFileInfo": false,
"MigrateMediaToMediaLibrary": false,
"LegacyFlatAssetTree": false,
"LegacyPermissiveMediaLibrarySubfolders": false,
"AssetRootFolders": {},
"UseDeprecatedFolderPageType": false,
"ConvertClassesToContentHub": "",
Expand Down
1 change: 1 addition & 0 deletions Migration.Tool.Common/ConfigurationNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class ConfigurationNames

public const string MigrateMediaToMediaLibrary = "MigrateMediaToMediaLibrary";
public const string LegacyFlatAssetTree = "LegacyFlatAssetTree";
public const string LegacyPermissiveMediaLibrarySubfolders = "LegacyPermissiveMediaLibrarySubfolders";
public const string AssetRootFolders = "AssetRootFolders";

public const string UseDeprecatedFolderPageType = "UseDeprecatedFolderPageType";
Expand Down
3 changes: 3 additions & 0 deletions Migration.Tool.Common/ToolConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ public class ToolConfiguration
[ConfigurationKeyName(ConfigurationNames.LegacyFlatAssetTree)]
public bool? LegacyFlatAssetTree { get; set; }

[ConfigurationKeyName(ConfigurationNames.LegacyPermissiveMediaLibrarySubfolders)]
public bool? LegacyPermissiveMediaLibrarySubfolders { get; set; }

[ConfigurationKeyName(ConfigurationNames.AssetRootFolders)]
public Dictionary<string, string>? AssetRootFolders { get; set; }

Expand Down
2 changes: 1 addition & 1 deletion Migration.Tool.Core.K11/Mappers/UserInfoMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ protected override UserInfo MapInternal(CmsUser source, UserInfo target, bool ne
target.UserName = source.UserName;
target.FirstName = source.FirstName;
target.LastName = source.LastName;
target.Email = source.Email;
target.Email = source.Email?.Trim();
// target.UserPassword = source.UserPassword;
target.SetValue("UserPassword", source.UserPassword);
target.UserEnabled = source.UserEnabled;
Expand Down
2 changes: 1 addition & 1 deletion Migration.Tool.Core.KX12/Mappers/UserInfoMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ protected override UserInfo MapInternal(KX12M.CmsUser source, UserInfo target, b
target.UserName = source.UserName;
target.FirstName = source.FirstName;
target.LastName = source.LastName;
target.Email = source.Email;
target.Email = source.Email?.Trim();
// target.UserPassword = source.UserPassword;
target.SetValue("UserPassword", source.UserPassword);
target.UserEnabled = source.UserEnabled;
Expand Down
2 changes: 1 addition & 1 deletion Migration.Tool.Core.KX13/Mappers/UserInfoMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ protected override UserInfo MapInternal(KX13M.CmsUser source, UserInfo target, b
target.UserName = source.UserName;
target.FirstName = source.FirstName;
target.LastName = source.LastName;
target.Email = source.Email;
target.Email = source.Email?.Trim();
// target.UserPassword = source.UserPassword;
target.SetValue("UserPassword", source.UserPassword);
target.UserEnabled = source.UserEnabled;
Expand Down
4 changes: 3 additions & 1 deletion Migration.Tool.Extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ You can customize class mappings to adjust the content model between the source

**Note**: Your mappings now replace the default migration functionality for all data classes (page types, custom tables or custom module classes) that you use as a source. Any class where you set at least one source field is affected. If you map only some fields from a source class, the remaining fields are not migrated at all.

If you need class mappings to alter several data classes in Kentico Xperience 13, consider [using AI tools to help you generate mappings quickly](https://docs.kentico.com/x/speed_up_remodeling_with_ai_guides).

### Remodel page types as reusable field schemas guide

For an end-to-end example of how to extract common fields from two page types from Kentico Xperience 13 and move them to a [reusable field schema](https://docs.kentico.com/x/D4_OD) shared by both web page content types in Xperience by Kentico follow this [migration guide](https://docs.kentico.com/x/remodel_page_types_as_reusable_field_schemas_guides) in the documentation.
Expand All @@ -356,7 +358,7 @@ This feature allows you to link child pages as referenced content items of a pag

This feature is available by means of content item director.

You can apply a simple general rule to link child pages e.g. in `Children` field or you can apply more elaborate rules. You can see samples of both approaches in [SampleChildLinkDirector.cs](./CommunityMigrations/SampleChildLinkDirector.cs)
You can apply a simple general rule to link child pages e.g. in `Children` field or you can apply more elaborate rules. You can see samples of both approaches in [SampleChildLinkDirector.cs](./CommunityMigrations/SampleChildLinkDirector.cs) or follow along with our [guide to transfer page hierarchy to the Content hub](https://docs.kentico.com/x/transfer_page_hierarchy_to_content_hub_guides).

After implementing the content item director, you need to [register the director](#register-migrations) in the system.

Expand Down
68 changes: 67 additions & 1 deletion Migration.Tool.KXP.Api/KxpMediaFileFacade.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using CMS.DataEngine;
using CMS.MediaLibrary;
using Microsoft.Extensions.Logging;
using Migration.Tool.Common;

namespace Migration.Tool.KXP.Api;

public class KxpMediaFileFacade
{
private readonly ILogger<KxpMediaFileFacade> logger;
private readonly ToolConfiguration toolConfiguration;

public KxpMediaFileFacade(ILogger<KxpMediaFileFacade> logger, KxpApiInitializer kxpApiInitializer)
public KxpMediaFileFacade(ILogger<KxpMediaFileFacade> logger, KxpApiInitializer kxpApiInitializer, ToolConfiguration toolConfiguration)
{
this.logger = logger;
this.toolConfiguration = toolConfiguration;
kxpApiInitializer.EnsureApiIsInitialized();
}

Expand Down Expand Up @@ -42,11 +47,72 @@ public void SetMediaFile(MediaFileInfo mfi, bool newInstance)
public MediaLibraryInfo GetMediaLibraryInfo(Guid mediaLibraryGuid) => MediaLibraryInfoProvider.ProviderObject.Get(mediaLibraryGuid);
#pragma warning restore CS0618 // Type or member is obsolete

public static bool IsDirectoryNameAllowed(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return false;
}

if (!name.All(x => char.IsLetterOrDigit(x) || new char[] { '-', '_', System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar }.Contains(x)))
{
return false;
}

// Reserved device names (Windows only)
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
string[] reserved = {
"CON", "PRN", "AUX", "NUL",
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"
};

string upper = name.ToUpperInvariant();
if (reserved.Contains(upper) || reserved.Any(r => upper.StartsWith(r + ".", StringComparison.Ordinal)))
{
return false;
}
}

return true;
}

private readonly HashSet<string> fixedFolderPaths = [];

#pragma warning disable CS0618 // Type or member is obsolete
public void EnsureMediaFilePathExistsInLibrary(MediaFileInfo mfi, int libraryId)
#pragma warning restore CS0618 // Type or member is obsolete
{
string? librarySubDir = System.IO.Path.GetDirectoryName(mfi.FilePath);
if (toolConfiguration.LegacyFlatAssetTree != true)
{
if (!string.IsNullOrEmpty(librarySubDir) && !IsDirectoryNameAllowed(librarySubDir))
{
// Try an automatic correction - replacing stretches of whitespaces with underscore.
// If that succeeds, inform user by a warning. Otherwise raise error and defer fixing the issue to user.
string fixedLibrarySubdir = Regex.Replace(librarySubDir.Trim(), @"\s+", "-");

string bypassMessage = $"If you want to bypass this, use {nameof(toolConfiguration.LegacyPermissiveMediaLibrarySubfolders)} appsettings option. Please refer to README for potential sideeffects.";

if (IsDirectoryNameAllowed(fixedLibrarySubdir))
{
librarySubDir = fixedLibrarySubdir;
mfi.FilePath = $"{librarySubDir}/{System.IO.Path.GetFileName(mfi.FilePath)}"; // Don't use Path.Combine, because it doesn't work correctly with subsequent internal CMS logic

if (!fixedFolderPaths.Contains(librarySubDir))
{
fixedFolderPaths.Add(librarySubDir);
logger.LogWarning($"Media library subfolder '{{MediaLibrarySubfolder}}' was transformed to XbyK allowed format '{{FixedMediaLibrarySubfolder}}'. {bypassMessage}", librarySubDir, fixedLibrarySubdir);
}
}
else
{
throw new InvalidOperationException($"Media library subfolder name {librarySubDir} is not in XbyK allowed format. It must contain only alphanumeric characters, underscores ('_'), hyphens ('-'), and cannot contain file names reserved by the operating system. {bypassMessage}");
}
}
}

#pragma warning disable CS0618 // Type or member is obsolete
MediaLibraryInfoProvider.CreateMediaLibraryFolder(libraryId, $"{librarySubDir}");
#pragma warning restore CS0618 // Type or member is obsolete
Expand Down
1 change: 1 addition & 0 deletions docs/Supported-Data.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Currently, the Kentico Migration Tool supports the following types of data:
- Page permissions are currently not supported by the Kentico Migration Tool and are not migrated. If you need page ACLs to be migrated, please [open an issue and request the feature](https://github.com/Kentico/xperience-by-kentico-kentico-migration-tool/issues/new?assignees=&labels=&projects=&template=feature_request.md).
- Migration of Page Builder content is only available for Kentico Xperience 13.
- If you are [migrating a Kentico 12 MVC application](https://github.com/Kentico/xperience-by-kentico-kentico-migration-tool/blob/master/docs/Usage-Guide.md#kentico-12-mvc) you can upgrade it to Kentico Xperience 13 using the Kentico Installation Manager (KIM) and then upgrade to Xperience by Kentico with Page Builder content.
- You can find examples of how to update your page retrieval code to work with Xperience by Kentico's _content items_ [in our guides](https://docs.kentico.com/x/upgrade_content_retrieval_code_guides).
- **Page attachments**
- Page attachments are not supported in Xperience by Kentico. Attachments are migrated into media libraries. See [`Migration.Tool.CLI/README.md - Attachments`](../Migration.Tool.CLI/README.md#Attachments) for detailed information about the conversion process.
- **Preset page templates** (_Custom page templates_ in Kentico Xperience 13)
Expand Down
Loading