Welcome to TechNet Blogs Sign in | Join | Help

Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

[Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6]

Advanced content deployment scenarios

Till now we have covered export and import of

  • single list items or documents
  • containers like folders, lists or webs
  • complete site collection

What we haven't covered is how to use this method to move items. Or how to control the which items from the export package need to get imported into the database.

Let's look on each of these topics separately.


1) Moving content using Content Deployment and Migration API

Export and import allows us to create a copy of items or containers but it does not automatically allow us to move content. But copying the content and then deleting the original content is very similar to a real move operation. the only problem here is that links pointing to the moved content will still point to the original content. So these would have to be adjusted.

And this is indeed what the Move operation in Site Manager does! It copies the content using the Content Deployment and Migration API, adjusts all links to point to the new content and then delete the original content.

Let's now look on a sample how this can be achieved by looking at a sample code for the move of a single image from an image library.

First step is to export the ListItem using the Content Deployment and Migration API as demonstrated in part 2:

// export an image from the Images Library

SPSite site = new SPSite("http://localhost:2000");
SPWeb web = site.OpenWeb("/SourceSubWeb");
SPList list = web.Lists["Images"];
SPListItem listItem = list.Items[0];

SPExportObject exportObject = new SPExportObject();
exportObject.Id = listItem.UniqueId;
exportObject.Type = SPDeploymentObjectType.ListItem;

SPExportSettings exportSettings = new SPExportSettings();
exportSettings.ExportObjects.Add(exportObject);
exportSettings.FileLocation = @"c:\export";
exportSettings.FileCompression = false;
exportSettings.SiteUrl = "http://localhost:2000";

SPExport export = new SPExport(expsettings);
export.Run();

// cleanup
web.dispose();
site.dispose();

The next step is to import the image from the export into a the desired new location similar to the steps described in part 3:

// import the image into the desired destination library

SPImportSettings importSettings = new SPImportSettings();
importSettings.SiteUrl = "http://localhost:2000";
importSettings.FileLocation = @"c:\export";
importSettings.FileCompression = false;
importSettings.RetainObjectIdentity = false;

SPImport import = new SPImport(impsettings);

EventHandler<SPDeploymentEventArgs> startedEventHandler = new EventHandler<SPDeploymentEventArgs>(OnStarted);
import.Started += startedEventHandler;

EventHandler<SPObjectImportedEventArgs> importedEventHandler = new EventHandler<SPObjectImportedEventArgs>(OnImported);
import.ObjectImported += importedEventHandler;

import.Run();

...

// event handler to assign a new parent to the orphaned image in the package

static
 void OnStarted(object sender, SPDeploymentEventArgs args)
{
   SPSite site = new SPSite("http://localhost:2000");
   SPWeb web = site.OpenWeb("/MyWeb");
   SPList list = web.Lists["Images"];

   SPImportObjectCollection rootObjects = args.RootObjects;
   foreach (SPImportObject io in rootObjects)
   {
      io.TargetParentUrl = list.ServerRelativeUrl;
   }

   web.dispose();
   site.dispose(); 
}

If you look at the code above you will see that there is one additional event handler registered with the import object. This event handler will fire every time a new object has finished importing. We will now use this event handler to finish the move operation by changing all links pointing to the original image to the new image and afterwards deleting the original image.

Lets look into the details.

The event handler gets the Url of the exported object and the Url of the imported object in the SPObjectImportedEventArgs:

  • SourceUrl
  • TargetUrl

Based on this information the following code will now give us the SPListItem object of the original image from the SourceUrl property.

string url = eventArgs.SourceUrl;
SPSite site = new SPSite(import.Settings.SiteUrl);
SPWeb web = site.OpenWeb(url, false);
SPListItem li = web.GetListItem(url); 

As next step we retrieve the number of backward links pointing to the original image. A backward link means that another object points to this object. A forward link on the other hand would point from the current object to a different object. Using the BackwardLinks collection we are able to identify all objects that a point to the exported object.

To adjust all links we need to follow all backward links pointing to the original object to adjust the links to point to the new object we get the links using the following code:

int count = li.BackwardLinks.Count; 
for (int i = count - 1; i >= 0; i--) 

   SPLink link = li.BackwardLinks[i]; 
   ...

Now we need to get the objects that are pointing to our source item by following the ServerRelativeUrl in each backward link:

SPLink link = li.BackwardLinks[i];
 
using (SPWeb rweb = linkSite.OpenWeb(link.ServerRelativeUrl, false)) 

   object o = rweb.GetObject(link.ServerRelativeUrl);
   ...
}

Now we known the object. The next step is to adjust the forward link. But to adjust the link we first have to cast the retrieved object to it's actually SharePoint type.

There are exactly two different object types that can create a link to our image: SPListItem and SPFile as these are the only object types that implement the ForwardLink property. And these objects also implement a method to adjust the forward link by replacing the link to the exported object with the link of the imported object:

object o = rweb.GetObject(link.ServerRelativeUrl);
if (o is SPFile)
{
   SPFile f = o as SPFile;
   f.ReplaceLink(eventArgs.SourceUrl, eventArgs.TargetUrl);
}
if (o is SPListItem)
{
   SPListItem l = o as SPListItem;
   l.ReplaceLink(eventArgs.SourceUrl, eventArgs.TargetUrl);
}

After we have successfully adjusted all links we can then savely delete the source image.

Here is the complete code for our event handler:

static void OnImported(object sender, SPObjectImportedEventArgs eventArgs)
{
   SPImport import = sender as SPImport;

   string url = eventArgs.SourceUrl;
   SPSite site = new SPSite(import.Settings.SiteUrl);
   SPWeb web = site.OpenWeb(url, false);
   SPListItem li = web.GetListItem(url);

   int count = li.BackwardLinks.Count;
   for (int i = count - 1; i >= 0; i--)
   {
      SPLink link = li.BackwardLinks[i]; 
      using (SPWeb rweb = site.OpenWeb(link.ServerRelativeUrl, false)) 
      { 
         object o = rweb.GetObject(link.ServerRelativeUrl); 
         if (o is SPFile) 
         { 
            SPFile f = o as SPFile; 
            f.ReplaceLink(eventArgs.SourceUrl, eventArgs.TargetUrl); 
         } 
         if (o is SPListItem) 
         { 
            SPListItem l = o as SPListItem; 
            l.ReplaceLink(eventArgs.SourceUrl, eventArgs.TargetUrl); 
         }
      }
   }
   li.Delete();

   web.dispose();
   site.dispose();
}

You can try this sample by creating a publishing site, create a sub web and upload an image to the "Images" library. Then create a page based on a page layout that has a Image Field and reference the uploaded image in this field. Now use the code shown above to move the image to the "Images" library in a different web.


2) How to control which of the objects in the export package need to be imported

The Content Deployment and Migration API itself does not provide a possibility to select specific objects for import. It will always import all objects included in the export package. In order to control which objects to import we again need to intercept the import using the "Started" event handler and remove all objects from the package that should not be imported.

This can be done using code similar to the following:

static void OnStarted(object sender, SPDeploymentEventArgs args) 

   
// assign new parents to the root objects if required.
   ...
   
// get the information about the location of the manifest file after decompression (if required)

   string tempFileLocation = args.TempDirectoryPath;
   
   // now we need get all manifest files from the SystemData.xml file in the tempdirectory path
   StringCollection manifestFilenames = GetManifestFilenamesFromSystemDataXmlFile(args.TempDirectoryPath + @"\SystemData.xml"); 

   // now we can manipulate the content of each of the manifest files and (e.g.) remove objects from the manifest.
   foreach (string manifestFilename in manifestFilenames)
   {
       DoTheRequiredManipulationsToTheManifestFiles(manifestFilename);
   }

The SystemData.xml file and the Manifest.xml files can be manipulated using normal Xml operations available in .Net framework.

Important to know when planning to manipulate the Manifest.xml files that each individual exported objects is reflected as a SPObject node. The SPObject nodes are sequentially serialized inside the manifest file. That means that subwebs are listed behind their parent webs but not as child xml nodes.

Now I see the following question coming up: Is it allowed to manipulate the Manifest.xml files?

The answer is yes! The xml schema of all the xml files in a content migration pack is officially documented and can (e.g.) be used by 3rd party companies to migrate content to WSS or MOSS:
http://msdn2.microsoft.com/en-us/library/bb249989.aspx

Published Thursday, August 30, 2007 7:11 PM by Stefan_Gossner

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

Friday, August 31, 2007 12:41 AM by Noticias externas

# SharePoint Content Deployment and the Migration API

Stefan is back! J Stefan is in Redmond this week, and we had a chance to catch up! In fact, I had dinner

Friday, August 31, 2007 5:32 AM by SharePoint, SharePoint and stuff

# Deep Dive in SharePoint Deployment und Migration API

Stefan Goßner taucht in einer vierteiligen Serie in die Tiefen der SharePoint Deployment und Migration

Friday, August 31, 2007 9:40 PM by Andrew Connell [MVP MOSS]

# Fantastic, incredibly detailed, content on content deployment by Stefan

Fantastic, incredibly detailed, content on content deployment by Stefan

Friday, August 31, 2007 10:00 PM by Mirrored Blogs

# Fantastic, incredibly detailed, content on content deployment by Stefan

[via Stefan Gossner ] This past week I was in Redmond teaching my WCM401 development class in an open

Monday, September 03, 2007 6:03 AM by Mirrored Blogs

# Content migration API "Deep Dive" by Stefan Goßner

Body: Great series of posts by Stefan on how to use the Content Migration API. Very timely for me as

Tuesday, September 11, 2007 11:02 AM by Joycode@Ab110.com

# SharePoint内容部署与迁移API

今天凌晨加班的时候偶然翻到 Stefan Gossner 的这几篇文章,强烈推荐给大家: Deep Dive into the SharePoint Content Deployment and Migration

Tuesday, September 18, 2007 9:52 AM by yair

# re: Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

Grate post, thanks.

But, what if my manifast.xml files weighs over 200 MB?

Tuesday, September 18, 2007 9:57 AM by Stefan_Gossner

# re: Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

Hi Yair,

it shouldn't matter. But if you are concerned about this use the FileMaxSize export property.

This also controls the size of the generated manifest.

So you will end up with multiple manifest files then.

Cheers,

Stefan

Monday, September 24, 2007 3:57 PM by Luis Sousa

# re: Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

Hi,

Your post was the best on content deployment with MOSS that I found on the internet. Better that MSDN documentation. :)

I wonder if you don't want to extend this article, with a part 5, where you can explain which of these features are available and work in websites with variations.

Congratulations!

Tuesday, September 25, 2007 5:49 AM by Connecting Systems the Microsoft Way

# MOSS / SharePoint Content Deployment

Great set of articles by Stefan Goßner : Deep Dive Into the SharePoint Content Deployment...

Friday, September 28, 2007 2:38 AM by Connected Systems Chilled Out Blog

# MOSS / SharePoint Content Deployment

MOSS / SharePoint Content Deployment

Tuesday, October 02, 2007 8:42 AM by Dragan Panjkov

# re: Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

Hi, Stefan!

Can you explain why I receive some error messages on trying to migrate content between two MOSS machines. First machine was installed on english SKU, with 3 variations: english, serbian-latin and serbian-cyrillic. On that installation I installed language pack for serbian-latin when it became available. Second machine (destination) is empty moss with english sku and serbian language pack.

Here are error details:

Could not find language Serbian (Latin, Serbia). at Microsoft.SharePoint.Deployment.ImportRequirementsManager.VerifyInstalledLanguage(String id, String name) at Microsoft.SharePoint.Deployment.ImportRequirementsManager.VerifyLanguage(SPRequirementObject reqObj) at Microsoft.SharePoint.Deployment.ImportRequirementsManager.Validate(SPRequirementObject reqObj) at Microsoft.SharePoint.Deployment.ImportRequirementsManager.DeserializeAndValidate() at Microsoft.SharePoint.Deployment.SPImport.VerifyRequirements() at Microsoft.SharePoint.Deployment.SPImport.Run()

Thanks!

Dragan

Thursday, October 04, 2007 4:20 AM by Stefan_Gossner

# re: Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

Hi Dragan,

you need to install the same language pack on both machines.

Cheers,

Stefan

Thursday, October 25, 2007 10:18 AM by Nita Arvind

# re: Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

Hi Stefan,

Me team is in a fix. We have some documents in a folder structure in a Content Management Application called Plumtree. Some of these Word documents have hyperlinks to eachother.

The requirement is for us to move these files and the folder structure as it is to a document library in sharepoint- this we are able to accomplish.

The next phase of the requirement was to update the hyperlinks in the documents with the new sharepoint URL. We are able to accomplish this in most cases using the SPfile.ReplaceLink function. But there are instances where this fails.

Eg: OLD URL: www.google.com?id=123

NEW URL: www.yahoo.com

It does nothing.

Is there something we might be doing wrong.

It works fine in cases where :

OLD URL: www.google.com/games.html

NEW URL: www.yahoo.com

Thursday, October 25, 2007 11:16 AM by Stefan_Gossner

# re: Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

Hi Nita,

please open a support case for this.

This needs to be analyzed in more details.

Cheers,

Stefan

Thursday, November 08, 2007 11:04 AM by Placelight Blog

# useful links for sharepoint 2007 customization

useful links for sharepoint 2007 customization

Friday, January 04, 2008 7:03 PM by AllTec

# re: Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

Hi

I want to migrate my content(documents in folder structure) from plumtree to MOSS 2007. How I should go ahead with this migration?

Thanks

AllTec

Monday, January 07, 2008 10:28 AM by Stefan_Gossner

# re: Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

Hi AllTec,

you would need to create a Content Deployment and Migration Package from the Plumtree content.

So you would need to write or purchase a tool that reads the content from Plumtree and creates the package. Then import the package into SharePoint.

The schema the package content has to follow is defined here:

http://msdn2.microsoft.com/en-us/library/bb249989.aspx

Cheers,

Stefan

Tuesday, January 22, 2008 8:20 AM by Paul

# re: Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

Hi stefan,

I like to know where the settings(e.g. authentification) for the target Sharepoint will be handled, if the target system is in a differnet domain than the source. I can´t see anything around the SPImport class that could help.

best regards

Paul

Tuesday, January 22, 2008 8:27 AM by Stefan_Gossner

# re: Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

Hi Paul,

there seems to be a misunderstanding.

SPImport can only be used against the local machine - not remotely.

Cheers,

Stefan

Monday, February 25, 2008 2:54 PM by Mike's Blog

# Deep Dive Into the SharePoint Content Deployment and Migration API

Stefan Goßner posted a great series of posts about the Content Migration API (formerly known as PRIME

Wednesday, February 27, 2008 11:47 AM by Michael

# re: Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

Is this an approach that could be used to export a particular content type and import it into a totally new content type?

Is there an event that could override which site column/field control the content is imported into?  Or will you end up importing the content - and then changing the content types?

Wednesday, February 27, 2008 11:50 AM by Stefan_Gossner

# re: Deep Dive into the SharePoint Content Deployment and Migration API - Part 4

Hi Michael,

you would need to modify the export package to achieve this before importing.

Cheers,

Stefan

Friday, April 11, 2008 7:37 PM by Stefan Goßner

# Deep Dive into the SharePoint Content Deployment and Migration API - Part 6

[ Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6 ] Requirements for a successful content deployment

Leave a Comment

(required) 
required 
(required) 
 
Page view tracker