Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Using SPExport and SPImport: Part 1

5.00/5 (2 votes)
28 Apr 2010CC (Attr 3U)7 min read 1   544  
Using SPExport and SPImport to copy or move a SharePoint web.

Introduction

Microsoft SharePoint Server 2007 and Windows SharePoint Services 3.0 are excellent tools to help companies organize their content and make it available to those who need it within the business. As companies change and reorganize, this information must be adaptable also. For instance, one division within a company may have started using a meeting site to help organize and collaborate on a project; however, the project may have been taken over by another division with its own site. How can all of the existing information be saved and moved? Although MOSS 2007 and WSS are great platforms, the tools available do fall short in this area. This article will explain options and a possible solution to allow webs to be copied or moved from one location to another.

Background

Let's start by defining a scenario for use in this article. Your company has created a SharePoint site structure similar to this

Root
AustinDallasFt. WorthEl Paso

Divsion1

Divsion3

Divsion5

Divsion7

Divsion2

Divsion4

Divsion6

Divsion8

This is, of course, very simplistic since in a real-world application, each site would potentially have many lists and subsites; however, this will give us a starting point for discussion. Now, consider when Divsion8 moves from El Paso to Austin. Of course, the site could remain at its present location in the overall site structure, yet, one would logically think to look for it under Austin, not El Paso, since it now resides there. A new subsite could be created under Austin and all of the content manually moved; however, this is a tedious and labor intensive process, and will not preserve audit information or version history.

Warning This project uses SharePoint 2010 Beta and Visual Studio 2010. Although it should work in previous versions, it has not been tested.

Options for copying or moving a site

When needing to copy or move SharePoint sites or webs, the options are really limited.

Central Administrator Backup and Restore

Backup and Restore

The SharePoint Central Administrator site offers the Backup and Restore function. However, as we can see, it is not very granular. You can back up the entire web application, not a single site or web.

Image 2

Stsadm

The venerable commandline tool stsadm.exe offers a more granular option for backup.

stsadm -o backup -url http://myserver/Elpaso/Division8 
       -directory C:\SPBackup -filename division8.bak - backupmethod full

More details about stsadm commands can be found here: http://technet.microsoft.com/en-us/library/cc263441.aspx

There is a limitation on size; backups using stsadm are limited to 15 GB or less, and if using SharePoint prior to SP2, you must also use setsitelock to prevent additions or updates to the site and its content while the backup is in progress. Also, alerts and workflows will not be preserved. To restore the site, you use the mirror command, restore. This is a time consuming two-step process, and can only be performed by someone with administrative rights and access to the stsadm application. Not for the average user.

SharePoint Designer

Using SharePoint Designer 2007, the site that is currently opened can be backed up using the Backup Web Site menu item under the Site -> Administration -> Backup menu.

Note: This option is curiously missing from SharePoint Designer 2010 beta.

To restore the backup to another location, you must first create the site, then restore it. A cumbersome three step process. Further limitations are that you must have access to SharePoint Designer, which not everyone will have.

SPExport/SPImport

Found in the Microsoft.SharePoint.Deployment namespace, these classes provide a method to basically backup and restore a site, web, list, and other objects in SharePoint, and are what the SharePoint Designer uses. To use SPExport or SPImport, you need to configure the process by using the respective settings classes.

C#
SPExportSettings settings = new SPExportSettings();
settings.FileLocation = "C:\SPBackup"; 
settings.SiteUrl = http://server/mySite
settings.FileCompression = true; 
settings.OverwriteExistingDataFile = true; 
settings.BaseFileName = "export";
SPExport export = new SPExport(settings); 
export.Run();

This code will create an archive file named export.cmp that contains information about the site collection located at http://server/MySite and place it in the folder C:\SPBackup. Setting FileCompression = false will produce a compressed folder structure. OverwriteExistingDataFile = true tells SPExport to overwrite any existing files. Setting it to false will produce an exception if the export.cmp file already exists. The reverse operation would be as follows:

C#
SPImportSettings settings = new SPImportSettings(); 
settings.FileLocation = "C:\SPBackup"; 
settings.BaseFileName = "export"; 
SPImport import = new SPImport(settings); 
import.Run();

A minor point of interest is when setting CommandLineVerbose = true in the Settings objects, it will write output to a console window so you can see the extensive operations that are occurring during an export or import. Although there is an event ProgressUpdated in both the SPExport and SPImport classes, it does not send this information. Instead, it only sends information about the total number of objects and how many have been processed.

These are just some basic settings. Now let's take a more detailed look at the process and how it can be used to copy or move a web.

Copy/Move Web

Using the SharePoint API, we can create classes that expose a simple interface for copying or moving a web.

C#
SPWeb web = new SPWeb("http://server/Elpaso/Division8", "http://server/Austin"); 
web.Copy(); 

The first parameter is the URL of the web to be copied. The second parameter is the URL of the destination. In this case, we will end up with http://server/Austin/Division8 that is a copy of what is at http://server/Elpaso/Division8.

Validate Source Web

The first thing that must be done is to verify the existence of the source web.

C#
private void ValidateSource()
{            
    try
    {
        Uri uri = new Uri(SourceURL);

        SourceSite = new Microsoft.SharePoint.SPSite(SourceURL);
        if(SourceSite != null)
        {
            SourceWeb = SourceSite.OpenWeb(uri.LocalPath);
            if(!SourceWeb.Exists)
            {
                SourceWeb.Dispose();
                SourceWeb = null;
                throw new ArgumentException("Source web is invalid");
            }
        }
    }
    catch(System.IO.FileNotFoundException)
    {
        // Could find the specified site
        throw new ArgumentException("SourceURL is invalid");
    }
}

An interesting note here is if we tried to call OpenWeb without passing the relative path of the web we are trying to open. As below, it would return the root web, http://myserver/Home in this case, and of course, SPWeb.Exists would return true.

C#
SourceWeb = SourceSite.OpenWeb();

A cumbersome alternative would be to search the SPWebCollection and attempt to match the name. Using the StringComparer.CurrentCultureIgnoreCase would be necessary since the names and the URL may not be the same case.

C#
string web = SourceURL.Replace(SourceSite.Url, "").Remove(0, 1);
if(SourceSite.AllWebs.Names.Contains(uri.LocalPath, 
              StringComparer.CurrentCultureIgnoreCase))
{
    SourceWeb = SourceSite.OpenWeb();
}

Validate destination web

The destination web must be validated also. If a web already exists with the same name, we must check its type. It if is not the same as the source, an exception will be generated during the import process, so it must be deleted. It isn't necessary to create a new web as one will be created during the import process. If the webs are the same type, the destination will be overwritten with the source during the import process.

C#
private void ValidateDestination()
{
    try
    {
        Uri uri = new Uri(DestinationURL);

        DestinationSite = new Microsoft.SharePoint.SPSite(DestinationURL);
        if(DestinationSite != null)
        {
            DestinationWeb = DestinationSite.OpenWeb(uri.LocalPath);
            if(DestinationWeb.Exists)
            {
                CompareTemplates();
            }
            else
            {
                throw new ArgumentException("Destination web is invalid");
            }
        }
    }
    catch(System.IO.FileNotFoundException)
    {
        // Could find the specified site
        throw new ArgumentException("Destination site is invalid");
    }
}

private void CompareTemplates()
{
    uint localID = Convert.ToUInt16(SourceWeb.Locale.LCID);

    string templateName = 
        GetTemplateName(SourceSite.ContentDatabase.DatabaseConnectionString,
        SourceWeb.ID, SourceWeb.WebTemplate);

    SPWebTemplate sourceTemplate = 
        SourceSite.GetWebTemplates(localID)[templateName];

    templateName = 
        GetTemplateName(DestinationSite.ContentDatabase.DatabaseConnectionString,
        DestinationWeb.ID, DestinationWeb.WebTemplate);

    SPWebTemplate destTemplate = 
        DestinationSite.GetWebTemplates(localID)[templateName];

    // If template are the same then the 
    // destination must be deleted.
    if(sourceTemplate.Name != destTemplate.Name)
    {
        RecursivelyDeleteWeb(DestinationWeb);
        DestinationWeb.Dispose();
        DestinationWeb = null;
    }
}

Finding the SPWebTemplate

When comparing the SPWebTemplate for webs, the obvious first place to look is the WebTemplate property. However, this does not give enough information for an accurate comparison. For instance, SPWeb.WebTemplate will return MPS for a web that was created with a Basic Meeting site template and STS for a Blank Site, but the actual template names are MPS#1 and STS#1, respectively. Where does this extra provisioning configuration information come from? Unfortunately, there does not appear to be any information in the SPWeb object that indicates the provisioning configuration; the only place this is available is in the WSS_Content database.

C#
private string GetTemplateName(string connString, Guid id, string webTemplate)
{
    string cmdText = string.Format("SELECT ProvisionConfig FROM " + 
                                   "dbo.Webs WHERE Id = '{0}'", id.ToString());
    int provisionConfig = 0;
    using(SqlConnection conn = new SqlConnection(connString))
    {
        using(SqlCommand cmd = new SqlCommand(cmdText, conn))
        {
            conn.Open();
            provisionConfig = Convert.ToInt32(cmd.ExecuteScalar());
        }
    }

    return string.Format("{0}#{1}", webTemplate, provisionConfig);
}

Here, we get the database connection string from the SPSite objects and look up the proper value in the Webs table. Of course, it should go without saying that accessing the database directly is not recommended, but, since this information is not available in the API, there is no choice.

Exporting the web

As stated above, the export process is configured using the settings class, SPExportSettings in this case.

C#
SPExportSettings settings = new SPExportSettings();
settings.FileLocation = ExportPath;
settings.BaseFileName = EXPORT_FILENAME; 
settings.SiteUrl = SourceSite.Url;

settings.ExportMethod = SPExportMethodType.ExportAll;
settings.FileCompression = true;
settings.IncludeVersions = SPIncludeVersions.All;
settings.IncludeSecurity = SPIncludeSecurity.All;
settings.ExcludeDependencies = false;
settings.ExportFrontEndFileStreams = true;
settings.OverwriteExistingDataFile = true;

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

What happens with the above code, however, is the entire site collection will be exported. Basically, a backup of the entire SharePoint site that is the root of the specified SiteURL. However, we are only interested in a single web. To export a single web, it needs to be added to the ExportedObjects collection. SPExport uses this collection to identify what needs to be exported. If this collection is empty, everything will be exported.

C#
SPExportObject expObj = new SPExportObject();
expObj.IncludeDescendants = SPIncludeDescendants.All;
expObj.Id = SourceWeb.ID;
expObj.Type = SPDeploymentObjectType.Web;
settings.ExportObjects.Add(expObj);

We specify the ID of the SourceWeb and set the type to Web. The SPDeploymentObjectType enumeration contains the members to identify: Site, Web, List, ListItem, File, and Folder.

Importing the web

C#
private void Import()
{
    if(DestinationSite == null)
        throw new ApplicationException("DestinationSite is null");

    SPImportSettings settings = new SPImportSettings();

    settings.FileLocation = ExportPath;
    settings.BaseFileName = EXPORT_FILENAME; 
    settings.IncludeSecurity = SPIncludeSecurity.All;
    settings.UpdateVersions = SPUpdateVersions.Overwrite;
    settings.RetainObjectIdentity = false;
    settings.SiteUrl = DestinationSite.Url; 
    settings.WebUrl = DestinationURL;

    SPImport import = new SPImport(settings);
    import.Run();
}

Enhancements and follow-up

Enhancements for future versions could be to provide the option to rename the web while copying or moving, and to update the navigation and quick launch for the parent site.

The code isn't very useful unless it can be used. This article provides background for creating the tool; a follow-up article will show how to expose it for usage.

History

  • Initial posting: 4/13/10.

License

This article, along with any associated source code and files, is licensed under The Creative Commons Attribution 3.0 Unported License