1. Almost everything that ImportExport does in TCM can be detected in TCM event system extension. You can do that by examining Session.ContextData collection. ImportExport stores Export-/Import-/UndoInstruction object there with the “tcm:ImportExportService” key. Constant for that string (as well as identifiers of other Tridion components, such as Publilsher or Workflow service which identify themselves by putting their Ids into ContextData) is publicly available in TcmApplicationId.ImportExportService in Tridion.ContentManager.Common.dll.

    Below is the prototype for event handlers that will intercept application data reads/writes. In case call is initiated from ImportExport, application data can be patched to replace TCM URIs to WebDAV URLs.

    public class CustomAppDataPatchingExtension : TcmExtension
    {
        public CustomAppDataPatchingExtension()
        {
            EventSystem.Subscribe<IdentifiableObject, LoadApplicationDataEventArgs>(PatchApplicationDataOnExport, EventPhases.Processed);
            EventSystem.Subscribe<IdentifiableObject, SaveApplicationDataEventArgs>(PatchApplicationDataOnImport, EventPhases.Initiated);
        }

        private void PatchApplicationDataOnExport(IdentifiableObject subject, LoadApplicationDataEventArgs eventArgs, EventPhases phase)
        {
            if (eventArgs.ApplicationId == "your appdata id" &&
                subject.Session.ContextData.ContainsKey(TcmApplicationId.ImportExportService) &&
                subject.Session.ContextData[TcmApplicationId.ImportExportService] is ExportInstruction)
            {
                // patch application data by replacing TCM URI with WebDAV URL (or whatever)
            }
        }

        private void PatchApplicationDataOnImport(IdentifiableObject subject, SaveApplicationDataEventArgs eventArgs, EventPhases phase)
        {
            if (subject.Session.ContextData.ContainsKey(TcmApplicationId.ImportExportService) &&
                subject.Session.ContextData[TcmApplicationId.ImportExportService] is ImportInstruction)
            {
                foreach (var applicationData in eventArgs.ApplicationDataCollection.Where(ad => ad.ApplicationId == "your appdata id"))
                {
                    // patch application data by replacing WebDAV URL back to TCM URI (or whatever)
                }
            }
        }
    }

    Downside of this approach is that there is no event is generated when global application data (with subjectId == null) is ported.

    Every action (read, save, localize etc.) that ImportExport does during export/import/undo is identified with this marker in ContextData. So you can use this marker when you want to disable some custom actions implemented via event system (such as initiating workflow or publishing of an item) that should only be done when user interacts with the Tridion, but not on bulk import.
    1

    View comments

  2. Last time we saw how to enable porting of custom application data. However, there is a challenge here: often application data contains TCM URIs to other Tridion items. After moving such application data to another system, TCM URIs will become invalid and that can break extensions that rely on this data.

    In general, application data is a byte array and only application that created it knows what’s inside it and how to work with it. However, if application data contains XML stored by Tridion.ContentManager.CoreService.Client.ApplicationDataAdapter, ImportExport can deal with it.

    Suppose we have following XML (where tcm:0-2-1 is the valid TCM URI of a publication):

    <Settings xmlns:custom="http://custom.com">
          <Item id="tcm:0-2-1" />
    </Settings>

    Let’s attach this XML to group item as application data with ID “custom:appdata” using CoreService client:

    var adapter = new ApplicationDataAdapter("custom:appdata", xmlElement);
    client.SaveApplicationData("tcm:0-11-65568", new[] { adapter.ApplicationData });

    In order to instruct ImportExport that application data has XML element or attribute with TCM URI, you need to specify tcmUriXPath attribute to the configuration file with XPath pointing to element with TCM URI:

    <ApplicationId pattern="MyCustomConfig" tcmUriXPath="//@id" />

    Let’s do export and examine exported package item (part of it):

    <PackageItem xmlns:tcm="http://www.tridion.com/ContentManager/5.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.sdltridion.com/ContentManager/ImportExport/Package/2013">
      <ApplicationDataCollection>
        <ApplicationData>
          <ApplicationId>custom:appdata</ApplicationId>
          <TypeId>XmlElement:Settings, http://custom.com</TypeId>
          <BinaryData>PFNldHRpbmdzIHhtbG5zPSJodHRwOi8vY3VzdG9tLmNvbSI+PEl0ZW0gaWQ9InRjbTowLTItMSIgLz48L1NldHRpbmdzPg==</BinaryData>
          <IsInherited>false</IsInherited>
        </ApplicationData>
      </ApplicationDataCollection>
      <Dependencies>
        <Dependency dependencyType="ApplicationData" itemUrl="/webdav/test" linkName="custom:appdata" xpath="/*[local-name() = 'Settings'][1]/*[local-name() = 'Item'][1]/@id" />
      </Dependencies>
    </PackageItem>

    Important part here is the dependency. It contains actual XPath to id attribute with TCM URI and WebDAV URL that corresponds to that TCM URI in the application data. If application data has multiple TCM URIs, each of them will be represented as separate dependency with unique XPath.

    Note that ApplicationData dependency is a bit special. ImportExport will not export item referenced in application data as normal dependency. However, a record for that item will be created in the mappings.xml. So before import you can map WebDAV URL in application data to another existing item on target TCM during import process and ImportExport will convert that mapped WebDAV URL to valid TCM URI and updates application data with new TCM URI before importing it.

    What to do if ImportExport cannot handle TCM URIs in application data for you (because it is not XML saved with ApplicationDataAdapter)? You can still do that using TCM event handler. More on that next time.
    0

    Add a comment

  3. As it was shown previous time, ImportExport can move application data along with items it is associated with. This is enabled for Tridion extensions out-of-the-box, but it is also possible to make ImportExport moving your custom application data.

    To enable custom application data porting, configuration file should be created and added to %Tridion_home%\config\ImportExport\ApplicationData folder. Here is the sample (you can also examine config files for Tridion extensions which are stored in that folder as well):

    <?xml version="1.0" encoding="UTF-8"?>
    <ApplicationDataCategories xmlns="http://www.sdltridion.com/ContentManager/ImportExport/Configuration/2013">
      <ApplicationDataCategory name="My appdata" description="My custom application data">
        <ApplicationId pattern="MyCustomConfig" />
        <ApplicationId pattern="custom:*" />
      </ApplicationDataCategory>
    </ApplicationDataCategories>

    Here I defined an application data category with name “My appdata” (it should be specified in Export/Import instructions) and longer description which will be shown in Content Porter UI as a hint when user hovers over:



    Category contains one or more patterns which ImportExport will use to find your application data. It can be specific application data ID (“MyCustomConfig”) or a group of IDs which starts with “custom:” prefix (so in extreme case you can specify “*” to always port all application data you have in your implementation).

    Having done that, you can export your application data:

    exportInstruction.ApplicationDataCategoryIds = new[] {"My appdata"};

    This config file should be present both on source TCM (to export application data) and on target TCM (to enable its import). Effectively, that is how ImportExport prevents you from importing application data of Tridion components that don’t installed on target TCM. If you don’t have, say, Translation Manager installed, there is no config file for porting its application data and thus, it won’t be imported even if import package has TM-related application data.
    0

    Add a comment


  4. Application data allows associating custom data with TCM items and storing it in Content Manager database. It is used by both custom extensions and by several Tridion components. Specifically, Experience Manager, External Content Library, Translation Manager and User Generated Content use application data.

    Export

    ImportExport allows moving application data of these Tridion extensions along with items it is associated with. When you do export, application data of all installed Tridion extensions will be exported by default.
    In order to control this process, there is ExportInstruction.ApplicationDataCategoryIds list. If you want to export application data that belongs to Experience Manager only, type this:

    exportInstruction.ApplicationDataCategoryIds = new[] {"Experience Manager"};


    (Note that in Content Porter UI application data category will not appear if there is no application data of such category in the selection.)
    In order to disable application data export, use empty list (equivalent of deselecting all checkboxes in the Content Porter UI):

    exportInstruction.ApplicationDataCategoryIds = Enumerable.Empty<string>();

    Again, in order to export application data of all extensions, do not specify anything (e.g. null).

    If extensions have so-called global application data (which is not associated with any item), such application data will be ported as well.

    Import

    On import, application data of all installed extensions will be imported by default. So if you exported application data of Experience Manager and Topology Manager, but target TCM doesn’t have Topology Manager installed, TM-related application data will not be imported. In the Content Porter UI you’ll see this warning:



    In order to specify application data you want to import, use ImportInstruction.ApplicationDataCategoryIds property:

    importInstruction.ApplicationDataCategoryIds = new[]
    {
        "Experience Manager",
        "External Content Library",
        "Translation Manager",
        "User Generated Content"
    };


    However, if some extensions are not installed on target TCM, their application data will not be ported, even if extension ID is specified.
    2

    View comments

  5. In order to make BluePrint resolving work, preparations need to be done during export: each localized or shared item stored in the package has the WebDAV URL of its primary BluePrint parent (local item in one of the parent publications where that item was created). You can find it if you’ll open package and check exported item XML.

    So ImportExport knows primary BluePrint parent WebDAV URL. It tries to find primary parent item on import TCM. If it exists, then we can find TCM URI of that item and then find it out in the context publication. Let’s do all the computations manually for localized folder /webdav/Child/Building%20Blocks/Cikkek from the example we used in previous article:



    ImportExport does following steps:

    • Read package item and find its primary BluePrint parent URL: /webdav/Parent/Building%20Blocks/Articles.
    • Check that item /webdav/Parent/Building%20Blocks/Articles exists and get its TCM URI: tcm:1-12-2.
    • TCM URI of the /webdav/Child publication is tcm:0-2-1 (that is known to ImportExport at the moment of resolving items inside publication).
    • Construct TCM URI of the folder in Child publication (change publication ID in the URI from step 2): tcm:2-12-2.
    • Convert TCM URI tcm:2-12-2 back into WebDAV URL: /webdav/Child/Building%20Blocks/Articles.
    That’s it, just several simple manipulations with TCM URIs and WebDAV URLs. Note that this algorithm will work even if folder has been localized and renamed to some other title. It can be localized multiple times in the complex BluePrint.

    There are several important enhancements were implemented to this feature in the 2013 SP1. Specifically:

    • In previous versions user has to have read permissions on primary BluePrint parent during export. It’s not necessary anymore, read permissions on the exported item itself is enough.
    • In previous versions user has to have read permissions on primary BluePrint parent during import. The same story – you don’t need any permission on parent items anymore.
    • In previous versions BluePrint resolving was done as a separate phase before actual import. So in order to successfully resolve BluePrint mapping both primary BluePrint parent and context publication has to exist before import. Because of this SDL recommended earlier to import BluePrint structure first and then do import of one publication at a time from top to bottom of the BluePrint. As of this version BluePrint resolving is performed during import just before the current item will be imported. That allows to do import multiple publications in the same BluePrint in single run.
    0

    Add a comment

  6. ImportExport completely relies on WebDAV URLs when it tries to find if the item in the package already exist on destination TCM. It can’t rely on TCM URIs as they are different on different instances of TCM. That usually works fine with local items. However, WebDAV URLs don’t work that well in combination with the BluePrint. Item can be localized and renamed down in the BluePrint. Typical example here is the localized components which have translated titles. When working with the TCM URI, it’s not an issue – the ItemId part of TCM URI is always the same for all BluePrint variants of an item. That allows you to find an item in any publication regardless of its title or titles of the parent organizational items.

    As an example, let’s import localized content into the child publication where all items are shared:


    Last time we discovered how to do mappings manually. Luckily, you don’t need to provide mappings manually for each and every localized item. First of all, let’s do import with default settings to see the result:


    New folder was created next to Articles folder and all localized items were imported as new local items in the Child publication. In order to fix this it is enough to enable BluePrint mappings feature in the ImportInstruction and Importer will discover localized and renamed items automatically:

    var instruction = new ImportInstruction { ResolveBluePrintMappings = true };

    Here is the result of import with BluePrint mappings enabled:


    All items are nicely localized and updated. No new local items were created.


    Important change in the new implementation is that in order to make BluePrint resolving work user doesn’t need access to parent publications (in previous versions read access on parent items was required).
    0

    Add a comment

  7. In previous posts (here and here) I've shown how to map publications and folders.

    Let’s make import even more fancy. Let's split items and import schema and components into different locations (they are stored in single folder in package):


    In order to achieve this, keep two mappings on publication and folder from previous article as is and add a separate mapping for a schema. So we will import schema into Old Schemas folder and all the rest content in the Old Articles folder:

    instruction.UserMappings = new[]
    {
        new Mapping2013(ItemType.Publication, "/webdav/Parent", "/webdav/Renamed"),
        new Mapping2013(ItemType.Folder, "/webdav/Parent/Building%20Blocks/Articles", "/webdav/Renamed/Building%20Blocks/Archive/Old%20Articles"),
        new Mapping2013(ItemType.Schema, "/webdav/Parent/Building%20Blocks/Articles/Article.xsd")
        {
            ImportContext = "/webdav/Renamed/Building%20Blocks/Archive/Old%20Schemas"
        }
    };

    Note that I mapped context of the schema, not the full URL. When you provide an import mapping (through API or mappings.xml), ImportExport will validate provided URLs: they have to point to existing TCM items. As of TCM 2013 SP1, this validation is performed only on the mapped part of the URL. We can’t do mapping of the full schema URL (as it doesn’t exist in the mapped location), but we can change its import context (by pointing to existing folder Old Schemas).


    After import we will get our items split on two folders as we expected.

    Next time we will talk about BluePrint mappings.
    0

    Add a comment

  8. Last time we ported content from Parent publication to another publication called Renamed.

    Let’s do another step and map Articles folder as well. Suppose, we want to import it into deeper subfolder such as /webdav/Renamed/Building%20Blocks/Archive/Old%20Articles (both Archive and Old Articles folders have to be created before import starts).




    Here is the code for import:

    instruction.UserMappings = new[]
        {
            new Mapping2013(ItemType.Publication, "/webdav/Parent", "/webdav/Renamed"),
            new Mapping2013(ItemType.Folder, "/webdav/Parent/Building%20Blocks/Articles", "/webdav/Renamed/Building%20Blocks/Archive/Old%20Articles")
        };
    string processId = client.StartImport(serverPackageName, instruction);

    Note that I did mappings of folder and publication. If the destination publication is different from the source one, you always need to map publication as well, even if you’re going to import single item and already provided a mapping for it. Reason for that are the item dependencies. Each repository-local object has several dependencies to other items in the context repository (examples are: context publication, parent organizational item, schema the component is based on, keywords that are used in the schema and so on). 

    When ImportExport create or update an item, it provides all the links on dependencies as WebDAV URLs. If publication is mapped, all links will be patched to point to items in the same repository; otherwise, TCM will detect cross-repository links inside repository-local item and will throw exception.

    Result of import is shown on the screenshot below:



    Items were moved into the Archive subfolder, but our Old Articles folder was renamed to Articles. It happened because the Articles folder is selected in the package. That means it has to be updated. So Importer made an update of the Old Articles folder with the content of Articles folder in the package. All properties of Old Articles folder were updated including its title.

    In order to fix this issue, let’s review our export selection. We need to select content of the Articles folder, but not the folder itself. Let’s fix our selection and do export again:

    var selection = new SubtreeSelection("/webdav/Parent/Building%20Blocks/Articles", includeRootContainer:false);

    Here we changed last parameter to keep Articles folder deselected and select its content only (the ‘Children only’ option in the Content Porter UI). Having new package with fixed selection, let’s do import again. This time we get what we wanted:


    Next time we'll take a look on individual items mappings.
    0

    Add a comment

  9. Mappings mechanism in Content Porter is a very powerful tool, but it’s too complicated and misunderstood by the community. Here I’ll try to explain how does it work and how to use it in several common situations.

    To illustrate details, let’s use following small dataset and try to port it:



    Export selection looks like this:

    var selection = new SubtreeSelection("/webdav/Parent/Building%20Blocks/Articles", true);

    Note that in Content Porter UI when you click on the folder, path to that folder (Parent publication and Building Blocks folder) will be selected as well. In general, if you don’t have any changes in these items I would suggest deselecting them: right-click on publication or folder and choose Selected children only.
    OK, having that items exported, let’s check how to apply mappings to them.

    Suppose on target destination publication has different name (‘Renamed’ instead of ‘Parent’). Here is how our mapping will look like in code:

    var client = new ImportExportServiceClient();
    var instruction = new ImportInstruction();
    instruction.UserMappings = new[]
        {
            new Mapping2013(ItemType.Publication, "/webdav/Parent", "/webdav/Renamed")
        };
    string processId = client.StartImport(serverPackageName, instruction);

    The exported package contains mappings.xml file which you can use to specify mappings. However, when doing automated imports it’s much more convenient to specify mappings in code.

    When mapping is done on organizational item it is applied to mapped item and to all items inside it. Effectively, we have following mappings in place (/webdav prefix removed to save some space):

    Original WebDAV URL
    Mapped WebDAV URL
    /Parent
    /Renamed
    /Parent/Building%20Blocks
    /Renamed/Building%20Blocks
    /Parent/Building%20Blocks/Articles
    /Renamed/Building%20Blocks/Articles
    /Parent/Building%20Blocks/Articles/Article.xsd
    /Renamed/Building%20Blocks/Articles/Article.xsd
    /Parent/Building%20Blocks/Articles/Article1.xml
    /Renamed/Building%20Blocks/Articles/Article1.xml
    /Parent/Building%20Blocks/Articles/Article2.xml
    /Renamed/Building%20Blocks/Articles/Article2.xml

    That will lead us to the following result after import:


    The important step here was to deselect publication. As it was deselected, the Renamed publication wasn’t updated and, thus, its title wasn’t updated to ‘Parent’ (which will lead to import error in our case as there is another publication exists with the same title).

    Next time we'll check how to map specific folders.
    3

    View comments

  10. There is a new feature in the export that controls the way how shared items are exported.


    Export shared items as shared

    That mode has always been implemented in Content Porter. Shared items are exported as normal items in that mode. In Tridion shared items aren’t stored as separate items: they are shadow copies of the nearest local item up in the BluePrint hierarchy with the fixed TCM URI links to make sure all links point to items in the same context publication. ImportExport saves this shadow copy as a real item (and exports its dependencies).

    During import shared item can be imported if it doesn’t exist on import system, but ImportExport will not update local item with shared content. To be precise, here is the table that summarizes rules ImportExport module uses while importing BluePrinted items:


    Local on export system
    Shared on export system
    Localized on export system
    Not on import system
    Create as local
    Create as local
    Create as local
    Local on import system
    Update
    No change
    Update
    Shared on import system
    Localize and Update
    No change
    Localize and Update
    Localized on import system
    Update
    No change
    Update

    This table adds another dimension to the rules ImportExport uses to decide on what to do with the selected item during import (additionally to the selected/dependent items).

    As table shows, selected shared item can only be created as local in the context publication (e.g. publication to which you’re performing import) which is rarely the expected behavior as it, effectively, flattens out the BluePrint hierarchy. In order to workaround that issue Content Porter docs always stress out that import should be performed from top to bottom of the BluePrint hierarchy to make sure items are created on the right level of the BluePrint.

    That export mode might be preferred in the situation when (1) you are the user with permissions restricted to context publication and (2) you’re porting local items while their dependencies are already moved on target TCM. For example, you’re porting local components to the environment where all the schemas and taxonomies (shared on current level) are in sync with your environment.

    Export shared items from owning publications

    New export mode introduced in the TCM 2013 SP1. The difference with the previous option is that shared item will never be exported. Instead, ImportExport will find the closest local item up in the BluePrint (e.g. local or localized item from which shared item gets its content) and exports that local item instead.

    This mode is specifically useful when porting bundles that often contain items from multiple publications. Suppose, you have a bundle with couple of shared pages and a bunch of shared components and component templates (all items types are shared from different publications). In that case you can export bundle and all items in it will be exported from the respective owning publications. Another use case is porting of components which use new keywords defined higher up in the BluePrint. In the new mode keywords will be ported from/to the parent publication. So after import on the destination TCM you’ll have new keywords in the taxonomy created where all other keywords defined and new components down in the BluePrint which use shared keywords.

    Keep in mind that this mode will lead to cross-publication exports. By default selected items and their dependencies (and dependencies of dependencies) are always exported. So even if you selected one single item, you may end up with the package that contains multiple publications as shown on the picture:



    Notice that single item in Child publication was selected, but there are many more items were exported from Parent publication.

    Now let’s move back to the table which shows how items are created or updated during import. Shared items are not updated, they can only be created. But in the new mode we don’t have shared items in the package anymore. Instead, we have parent local (localized) item in the package. So if it was selected, it will be updated during import. In the case with the bundle porting, both bundle and items inside it will be updated during import.

    Note that only closest BluePrint parent of a shared item is exported. If item was localized multiple times, only one local copy will be exported. So if item doesn’t exist on destination TCM, local item will be created instead of localized one. That is still might be undesired behavior. If someone of you wants have another export mode which will export whole BluePrint chain of the selected item (all local copies up to the local item), feel free to vote for it in the comments (preferably with the use case when it will be useful).

    Final note: new mode is only available when porting data from TCM 2013 SP1 to TCM 2013 SP1. It’s not possible to port shared items in owning publications from/to previous versions of TCM.
    1

    View comments

Loading