Die “SharePoint Content and Migration API” bietet Entwicklern die Möglichkeit SharePoint Inhalte, wie bspw. Websites, Listen und Listeneinträge, zu exportieren und an einer anderen Stelle wieder zu importieren. Diese API ist das Fundament, auf dem auch die SharePoint OOTB –Mechanismen Import und Export basieren.

Für einen Kunden mit einer komplexen Farmumgebung habe ich einen erweiterten “Quick-Deployment”-Mechanismus umgesetzt. Einzelne WebCMS-Seiten sollen nach erfolgreichem Durchlaufen eines speziellen Genehmigungs-Workflows auf diverse Ziele (Websites in anderen Website Collections) verteilt werden. 

Das programmatische Kopieren von Listeneinträgen über Web Applikations-Grenzen hinaus ist nicht zu empfehlen. Es funktioniert nur, wenn die Application Pools Identities identisch sich und dies ist nicht immer gegeben. Aus diesem Grund fiel die SharePoint API getriebene Option schon einmal weg. Zudem wollte ich den Mechanismus nicht auf eine Maschine beschränken und Inhalte auch über Farm-Grenzen hinaus bereitstellen können

Dank Stefan Goßner’s hervorragenden Beitrag zu diesem Thema stand meine Technology-Entscheidung schnell fest.  Die “SharePoint Content Deployment and Migration API” ist exakt das Mittel der Wahl, um diese Anforderungen umzusetzen.

Da trotz meines Mechanismus das OOB Content Deployment funktionieren soll, musste ich beide Mechanismen in Einklang bringen. Dazu ist es wichtig die Objekt-ID auf dem Ziel beizubehalten, da ansonsten das Content Deployment beim nächsten Durchlauf das Objekt nicht mehr findet.

Nachfolgend habe ich den Export und den Import via ”SharePoint Content Deployment and Migration API” kurz skizziert.

Export der Seite aus der Quelle:

 //get the item (page) to export
 SPListItem listItem = sourceWeb.GetListItem(itemUrl); //item to export

 SPExportObject exportObject = new SPExportObject();
 exportObject.Id = listItem.UniqueId;// set the id of the export object

 //export object of type SPListItem
 exportObject.Type = SPDeploymentObjectType.ListItem;
 exportObject.IncludeDescendants = SPIncludeDescendants.None;
 exportObject.Url = itemUrl;// set the URL of the page

 SPExportSettings exportSettings = new SPExportSettings();
 exportSettings.ExportObjects.Add(exportObject);
 //Export dir should be an UNC path to support import beyond mashine boundaries
 exportSettings.FileLocation = ExportDir;
 //writing export informations to this file
 exportSettings.LogFilePath = ExportDir + "\\page_exporter.log";//logging information 
 exportSettings.FileCompression = false;// less space, but more proces time  
 exportSettings.SiteUrl = dataSite.Url;

 exportSettings.ExportMethod = SPExportMethodType.ExportAll; //full export (dependencies)
 exportSettings.IncludeVersions = SPIncludeVersions.LastMajor;//major, minor, all 

 SPExport export = new SPExport(exportSettings);
 export.Completed += new EventHandler<SPDeploymentEventArgs>(ExportCompleted);//send success email for example 
 export.Error += new EventHandler<SPDeploymentErrorEventArgs>(Export_Error); //send error email for example
 export.Run();// process export

 

Nach dem erfolgreichen Export der WebCMS-Seite sind die Export-Dateien in dem definierten Verzeichnis (Variable ExportDir) einsehbar. Um über Maschinen-Grenzen agieren zu können, sollte der Pfad ein UNC-Pfad sein, auf dem die Application Pool Identity zugriff hat.

Der Import für einzelne Ziele kann unterschiedlich lange dauern. Grund dafür ist beispielsweise die Systemauslastung. Die Rückmeldung, dass der Import fertig ist, ist technisch nicht ohne weiteres abfangbar, da der Import ggf. auf einer entfernten Maschine aus Dienst ausgeführt wird. Durch die zeitliche Varianz der Importe ist es wichtig, dass für jedes Ziel die Export-Dateien in ein separates Verzeichnis kopiert werden, da sonst Zugriffsproblem auftreten können. Dazu reicht es die Exportdateien in Unterverzeichnisse abzulegen und jedem Import sein Unterverzeichnis mitzugeben.

image

 

Wenn man die Variante über UNC-Pfad nicht möchte, dann kann man auch dem eigenen “Import-Webservice” das komprimierte Export-Paket als Byte[] mit geben.

Im Fehlerfall kann von der SharePoint-Maschine eine Email bspw. an einen Administrator geschickt werden. Der Administrator kann anschließend in der Log-Datei “page_exporter.log” (exportSettings.LogFilePath) die Fehler-Ursache des fehlgeschlagenen Exports einsehen und entsprechend reagieren.

image

 

Import der Seite in ein Ziel:

Nach dem erfolgreichen Exportieren der Seite kann über einen eigenen Webservice der Import auf das Ziel angestoßen werden. Der Webservice kann mit einem WSP in das “_vti_bin” oder “_layouts”-Verzeichnis ausgerollt werden und ist somit Farm-weit verfügbar. Über die aufgerufene Webservice-URL kann exakt das Import-Ziel definiert werden. (Zum Beispiel http://site collection>/website/_layouts/custom_importer.asmx)

Bevor das Export-Paket in die Ziel-Website Collection importiert werden kann, muss es programmatisch modifiziert werden. Grund dafür ist die Änderung des übergeordneten Objektes, unter dem das Export-Objekt eingefügt werden soll.  Ohne die Modifikation wüsste das zu importierende Objekt, wo es eingeordnet werden soll. Diese Anpassung kann im “Started”-Event der Klasse SPImport  durchgeführt werden.

        void OnStarted(object sender, SPDeploymentEventArgs args)
        {
            try
            {
                // ensure permissions to change objects of the import package
                SPSecurity.RunWithElevatedPrivileges(delegate
                {
                     using (SPSite targetSite = new SPSite(TargetWebUrl))
                     {
                         using (SPWeb targetWeb = targetSite.OpenWeb())
                         {
                             SPList list = targetWeb.Lists[PublishingWeb.GetPagesListName(targetWeb)];

                             SPImportObjectCollection rootObjects = args.RootObjects;
                              //foreach object in the import package
                             foreach (SPImportObject io in rootObjects)
                             {
                                 //change the parent object URL to the list URL of the target site 
                                 if (io.SourceUrl.ToLower().Contains(Settings.ImportPageWebRelativeUrl.ToLower()))
                                 {
                                     io.TargetParentUrl = String.Format("{0}/{1}/", targetWeb.ServerRelativeUrl, list.Title);
                                 }
                             }                             
                         }
                     }
                 });
            }
            catch (Exception ex)
            {
              //ToDo: logging
            }
        }

Importierung des SharePoint List Items:

           SPImportSettings importSettings = new SPImportSettings();
                    importSettings.SiteUrl = TargeteSiteUrl; //website url of target
                    importSettings.FileLocation = Settings.ExportDir;// import specific sub dir under the export dir
                    importSettings.FileCompression = false; // the same settings as exporter uses
                    importSettings.RetainObjectIdentity = true;//Keep ID, so content deployment will still work
                    importSettings.CommandLineVerbose = true;
                    importSettings.IgnoreWebParts = false;//import webparts or not?
                    //log import issues to this file
                    importSettings.LogFilePath = Settings.ExportDir + "page_importer.log";
                    importSettings.UpdateVersions = SPUpdateVersions.Overwrite;
                    
                    SPImport import = new SPImport(importSettings);

                    //before importing, we have to prepare the data package
                    EventHandler<SPDeploymentEventArgs> startedEventHandler = new EventHandler<SPDeploymentEventArgs>(OnStarted);
                    import.Started += startedEventHandler;
                    
                    import.Completed += new EventHandler<SPDeploymentEventArgs>(import_Completed);//send success email
                    import.Error += new EventHandler<SPDeploymentErrorEventArgs>(import_Error);// send error email

                    //ensure permission to do this operation
                    SPSecurity.RunWithElevatedPrivileges(delegate
                    {
                         import.Run();
                    });

 

Wurde der Import fehlerfrei durchgeführt, kann bspw. im “Completed”-Event eine Email an den Autor der Seite gesendet oder auch eine Rückmeldung an einen eigenen Import/Export-Manager via Webservice geschickt werden. Sollte der Import fehlgeschlagen sein, kann auch hier eine Email an einen Administrator geschickt werden. Dieser kann in der Log-Datei “page_importer.log” (importSettings.LogFilePath) im jeweiligen Import-Verzeichnis die Ursache einsehen und ggf. beheben.

Fazit:

Die “SharePoint Content Deployment and Migration” API ist eine sehr elegante Option, Inhalte Workflow-gesteuert bis über Farm-Grenzen zu verteilen. Bei der Verteilung können sogar Abhängigkeiten, Versionen, Webparts, und weitere Bereiche mit berücksichtigt werden. Entwickler haben somit die Möglichkeit OOTB-Mechanismen wie bspw. “Quick Deployment” zu erweitern und auf Kundenwünsche zuzuschneiden.

Leave a comment

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Time limit is exhausted. Please reload the CAPTCHA.