Skip to content

meridiumlabs/episerver.migration

Repository files navigation

Meridium EPiServer Migration

A library which aims to help the EPiServer developer to migrate content from an old EPiServer site (5+) to a new EPiServer 7.5+ site.

What is it?

This project is an effort to reuse some common patterns that emerge when a new site is developed and content from the old site must be migrated to the new.

The main feature is an API which is used to specify mappings of page types and their properties from the old site to the content types of the new site.

Please read the following conventions we use to decide if this library will be usable in your scenario.

Conventions

Import

  • PageSaved, PageChanged, PageChangedBy and PageCreatedBy are set to their original values on publish

How to install

Currently there is no pre-built artifact you can fetch from NuGet or download. There is however a simple post build script which zips the necessary files for you.

So you need to clone this repository and build it locally.

Build

After you have cloned the repository you'll probably want to update the NuGet references to EPiServer from EPi's NuGet feed to the specific version your project is using to avoid any version conflicts.

When you successfully build the project, a post-build event is triggered which executes the build.ps1 PowerShell file. This script creates a build folder in the project and a zip archive - migration.zip - in that folder containing the files you need.

migration.zip
|-- Migration
|   `-- Migrate.aspx                      # Migration Web UI 
|-- lib
    |-- HtmlAgilityPack.dll               # Dependency
    |-- HtmlAgilityPack.pdb
    |-- Meridium.EPiServer.Migration.dll  # Where all the good stuff lives
    `-- Meridium.EPiServer.Migration.pdb  # Debug stuff

If you get a PowerShell error during the post-build event you'll probably need to set the PowerShell execution policy. Note that Visual Studio is a 32-bit application and that PowerShell 32/64 bit have different settings.

Using it

When you have the zip-file, we recommend that you create a new project in the solution of your new site which will receive the result of the migration, e.g. Your.New.Site.Migration.

Extract the zip here and reference the Meridium.EPiServer.Migration assembly and the project where your content types live.

What you need to do now is to define the mappings from page types in the old site to the new shining content types of your new site.

Define mappings

The following commented code example will explain the main building blocks.

// The migration library will scan the assemblies of the bin folder
// looking for classes marked with the MigrationInitializer attribute
// and a public static void Initialize method which it will happily call.
[MigrationInitializer]
public static class MigrationDefinitions {
  public static void Initialize() {
    // Register your mappings using the static Register method of the 
    // MapperRegistry class. This method accepts a variable parameter list
    // if IPageMapping instances
    MapperRegistry.Register(

      // Hopefully you will not need to implement the IPageMapper interface
      // yourself, but use the PageMapper fluent API instead. The starting
      // point of the API is the Define method which accepts a name for the
      // mapping
      PageMapper.Define("Mapping name")

        // The Map method defines a mapping for a page type. The new strongly 
        // typed content type is specified as the type argument and the name 
        // of the old page type is given as the first argument. 
        .Map<NewsListPage>("[Old] NewsArchive",

          // The second argument to Map is an Action<SourcePage, TNew> callback
          // in which you specify how properties are mapped. The SourcePage
          // class contains the properties of the page being migrated and has
          // a couple of helpful methods you can use to get the values. 
          // This example uses the GetValueWithFallback method which falls 
          // back to the next property in the list when a previous value does
          // not exist or is null
          (s, d) => d.Heading = s.GetValueWithFallback<string>("Header", "PageName"))

        .Map<NewsPage>("[Old] NewsItem",
          (s, d) => {
            d.Heading = s.GetValue<string>("Header");
            // Sometimes you'll need to clean up content from the old pages
            // This is best done via extension methods. A few are defined in
            // the library which may or may not be of use to you
            d.Introduction = s.GetValue<string>("Ingress").CleanupForIntroduction();
            d.MainBody = s.GetValue<XhtmlString>("Bread").CleanupForMainBody();
        })

        // You may leave the Action argument if no properties are mapped
        .Map<ContainerPage>("[Old] Folder")

        // The Default method defines the mapping for all pages where a mapping
        // cannot be found.
        .Default<ArticlePage>(
          (s, d) => {
            d.Heading = s.GetValueWithFallback<string>("MainHeader", "Header", "PageName");
            d.Introduction = s.GetValue<XhtmlString>("Ingress").CleanupForIntroduction();
            d.MainBody = s.GetValue<XhtmlString>("MainBody").CleanupForMainBody();
        }));
  }
}

The MapperRegistry.Register method accepts a list of IPageMappers. This can be useful if you want to separate the mappings, e.g. when you are importing content from more than one site or the original site had subsections with their own page types.

Hook on to events

The import/migration process exposes a few events which you can hook on to. To register on an event, use the following syntax in your migration project:

MigrationHook.RegisterFor<BeforePageImportEvent>( (evt, log) => {
  // Do stuff... Event data is in the evt object and 
  // you can write messages to the log if you need to 
});

The argument to the RegisterFor method is an Action<TEvent, IMigrationLog> action callback. The first argument of this function is an object containing the event data. The second is an instance of IMigrationLog which you may use to output messages to the log.

Event data objects are derived from the DataImporterEvent<T> class which has a property T ImportData holding the information which is relevant for the event type.

The following events are available:

Event type ImportData type Description
BeforePageImportEvent ContentImportingEventArgs Fires before a page is imported
AfterPageImportEvent ContentImportedEventArgs Fires after a page is imported
BeforeFileImportEvent FileImportingEventArgs Fires before a file is imported
AfterFileImportEvent FileImportedEventArgs Fires after a file is imported
BeforePageTransformEvent PageData Fires before a page transformed. The PageData is the old page which will be transformed to a new type.
AfterPageTransformEvent ContentReference Fires after a page is transformed. The ContentReference is a reference to the new page.

Execute migration

When you have built and tested your mappings it is time to execute the migration. These are the steps.

1. Export content from old site(s)

Use EPiServer's export tool under Admin/Tools in the admin interface. Export the content you need in one or more packages.

Copy the export packages to the server where the migration will take place.

2. "Install" your migration dll to the target site

Move your migration dll which contains the page mappings together with the Meridium.EPiServer.Migration and the HtmlAgilityPack dlls to the bin folder of the target site. Copy the Migrate.aspx file a new directory called ~/migration/.

If you want your packages to be auto discovered by the migration tool you must put them in the ~/migration/packages/ directory.

3. Open the Migrate.aspx page in a browser

The Migrate.aspx page consists of three steps: Import, Migrate and Clean up.

3.1 Import

The Import step performs an import of the pages which were exported from the other site in step 1. The input needed from you is the page id of the page under which the content will be imported and the absolute path on the server of the package to import. If you put the packages in the ~/migration/packages/ directory, this is handled for you and you get a nice drop down list. When you're done, click Import to start the import.

During the import the log should update on the bottom of the page.

3.2 Migrate

The next step is to perform the actual migration of content from old page types to the new types. Specify the id of the root page of the imported pages in step 3.1 and select the page mapping you want to use from the drop down list. Click the Migrate button to start the migration.

3.3 Clean up

The last step is to delete the imported page types, since they are probably no longer relevant to keep around. Click the Delete page types button to delete the imported page types. Leave the Logging only checkbox checked to only log the page types which would be removed.

The heuristic used when deciding which page types to remove is

  • the page type has no associated model (ModelType property is null)
  • the page type name does not start with "sys"

4. Clean up

When you are done you can

  • Delete the migration directory
  • Delete the dll:s you copied in step 2
  • Delete the import packages.

Additional resources

About

A library which helps in mapping content from an old to a new EPiServer site

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published