Changes to the Copy Functionality

In earlier EPiServer versions copying a page did not mean that you copied all of the pages contents with it. Today you do. When copying a page you will now also copy all items in the folder belonging to the page.
This is also true when mirroring a site or importing/exporting large numbers of pages. In practicality this means that the copying process will be slower, but also that it is a lot more useful than before.

Transfer functionality

The page copy, import / export and page mirroring functionality uses the transfer functionality to transfer pages and files to the destination. When a page is copied all links to pages and files in the pages folders are replaced with links based on the pages GUID instead of the page id, this means that a link points to the same content even if the content is on another site (if that is the desired behavior, as in mirroring).

Copy Page

Programmatically

The functionality to copy pages has been enhanced to allow the developer to use the copy thread by using the function EPiServer.DataFactory.Instance.Copy(PageReference, PageReference, bool) with the parameter allowThreading set to true. This method is going to return when the first page is copied to the destination, and a context of the working thread can be found in the static method: EPiServer.Core.Transfer.CopyThread.GetThreadsForUser(string) where the string is the username of the user that invoked the copy function.

Import/Export

The importing and exporting pages are based on the same transfer functionality as the copy. The old file encrypting used in epi4 files is removed and a zipped XML file is produced by the export.

It is possible to view the contents of an .episerverdata file by changing the file extension to .zip and opening it with a zip capable program (such as Winzip or Winrar). The files in the package can also be extracted.

Old Export Files (epi4)

Importing/Exporting Programmatically

To use the import and export functionality programmatically it is possible to add exportable contents to an instance of the Exporter type in the EPiServer.Enterprise namespace.

ex1. Example of exporting all content from the start page and all it’s sub pages recursively.

CopyC#
// Create a new exporter 
DataExporter Exporter = new DataExporter();

// Create an output stream
string exportFile = Path.GetTempFileName();
Exporter.Stream = new FileStream(exportFile, FileMode.Create, FileAccess.Write, FileShare.None);

// Add pages to the export package, here alla pages below the startpage
Exporter.SourceRoots.Add(new ExportSource(PageReference.StartPage, 
    ExportSource.RecursiveLevelInfinity));

// Export all pages
Exporter.Export();

// Close the exporter
Exporter.Close();

// Close the out file 
Exporter.Stream.Close();

ex2. Example of importing the content package described in ex1 to the wastebasket.

CopyC#
//Create an importer
DataImporter Importer = new DataImporter();
// Create a source file based on the file name abow
Importer.Stream = new FileStream(exportFile, FileMode.Open);
// Set a destination root 
Importer.DestinationRoot = PageReference.WasteBasket;
// Import the content
Importer.Import();
// Close the source file 
Importer.Stream.Close();

Mirroring

The mirroring functionality is based on the same transfer functionality as the copy mechanism, but uses the EPiServer.Core.Transfer.DefaultPageTransferContext.IsCopyByReference flag to indicate that all content should be restored and not copied.

When using page mirroring and the identifier (eg. The page GUID) is not represented in the transfer package, a place holder is created for this page with the name "Copy Page Prototype - used by the copy functionality".
This page has to be included in this or another mirroring channel to get the consuming website to work correctly.

Mirroring To Local Host

When mirroring to a local host a lockup table is written to remember how to replace the GUIDs so that the mirroring package doesn’t restore the source pages instead of the destination page.

No actual content will be transferred for the mirrored pages, only references to the content.

Add Transfer Functionality Custom Properties

To add functionality to transfer a custom property it is possible to add an event handler for this property. When an export package wants to export a property, an EPiServer.Enterprise.DataExporter.ExportPropertyEvent is triggered.

ex3. Example of handling a custom export event.

CopyC#
public void AddEventHandler()
{
    // Add the functionality to the exporter
    EPiServer.Enterprise.DataExporter.ExportPropertyEvent += 
        new EventHandler<TransformPropertyEventArgs>(
            PropertyCustomPropertyTransform.ExportEventHandler);

    // Add the functionality to the importer 
    ImportPropertyEvent += 
        new EventHandler<TransformPropertyEventArgs>(
            PropertyCustomPropertyTransform.ImportEventHandler);
}

// Create an event handler
public class PropertyCustomPropertyTransform
{ 
    public static void ExportEventHandler(object sender, TransformPropertyEventArgs e)
    {
        IPageTransferContext transferContext = sender as IPageTransferContext;

        // Check if the property should be handled by this event handler
        if ( transferContext == null || 
             e == null || 
             e.IsHandled || 
             e.PropertySource == null || 
             e.PropertySource.TypeName == null || 
             e.PropertySource.TypeName.CompareTo("Developer.CustomProperty") != 0)
        {
            // Do nothing if the event should not be handled by this event handler
            return;
        }

        // modify the exportable format
        e.PropertySource.Value = "exported" + e.PropertySource.Value;
        // Tell the other event handler not to handle this property
        e.IsHandled = true;
    }

    public static void ImportEventHandler(object sender, TransformPropertyEventArgs e)
    {
        IPageTransferContext transferContext = sender as IPageTransferContext;
        // Check if this event handler should handle this property 

        if (transferContext == null || 
             e == null || 
             e.PropertySource == null || 
             e.PropertySource.Type != PropertyDataType.String)
        {
            //  Do nothing if the property should be imported by another handler
            return;
        }

        CustomProperty propDestination = e.PropertyDestination as CustomProperty;

        propDestination.Value = e.PropertySource.Value.Replace("exported", "");
        e.IsHandled = true;
    }
}