Da man mit Export-SPWeb bzw. Import-SPWeb bzw. im GUI über granular Backup/Restore nur ganze Document Libraries kopieren kann hab ich mich auf die Suche gemacht und bin hierauf gestoßen. Da das dort aufgeführte “Programm” eher nicht so 1:1 zu verwenden war (und auch bei den Foldern beispielsweise Author/Timestamp nicht setzt) und ich die Geschichte mit Excel, Versionen und verschiedenen Modi etc. nicht benötigte habe ich es entsprechend umgeschrieben (mit Hilfe). Ohne wirklich Errorhandling und kaum Logging – aber es hat einwandfrei funktioniert (SharePoint ist offenbar relativ relaxed wenn man Dinge added die es schon gibt):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using Microsoft.SharePoint;
namespace Gallauner
{
class SharepointListCopy
{
static void Main(string[] args)
{
string sSourceSite = "https://oldsite.domain.com";
string sSourceWebName = "SomeWeb";
string sSourceListName = "SomeList";
string sSourceFolderName = "SomePath/MorePath/LastPath";
string sDestSiteName = "https://newsite.domain.com";
string sDestWebName = "SomeNewWeb/SubWeb";
string sDestListName = "SomeNewList";
string sDestFolderName = "";
SPSite oSourceSite;
SPList oSourceList;
SPWeb oSourceWeb;
SPListItemCollection oFolders;
Microsoft.SharePoint.Administration.SPWebApplication oWebApp;
// open source site/web/list
oSourceSite = new SPSite(sSourceSite);
oWebApp = oSourceSite.WebApplication;
oWebApp.FormDigestSettings.Enabled = false;
if (sSourceWebName == "")
oSourceWeb = oSourceSite.RootWeb;
else
oSourceWeb = oSourceSite.OpenWeb(sSourceWebName);
oSourceList = oSourceWeb.Lists[sSourceListName];
if (oSourceList != null)
{
// search source folder
oFolders = oSourceList.Folders;
foreach (SPListItem oFolder in oFolders)
{
if (oFolder.Folder.Url == sSourceFolderName)
{
// copy folder
CopyFilesAndSubFolders(oLog, oFolder.Folder, oSourceWeb,
sDestSiteName, sDestWebName, sDestListName, sDestFolderName);
}
}
}
oWebApp.FormDigestSettings.Enabled = true;
}
private static void CopyFilesAndSubFolders(Logfile oLog, SPFolder oFolder,
SPWeb oSourceWeb,string sDestSiteName,
string sDestWebName,string sDestListName,
string sDestFolderName)
{
SPSite oDestSite;
SPWeb oDestWeb;
SPList oDestList;
SPFolder oDestFolder, oNewDestFolder;
SPFile oFileCopy;
SPUser oUser;
// open site/web/list
oDestSite = new SPSite(sDestSiteName);
if (sDestWebName == "")
oDestWeb = oDestSite.RootWeb;
else
oDestWeb = oDestSite.OpenWeb(sDestWebName);
oDestList = oDestWeb.Lists[sDestListName];
// find folder
oDestFolder = null;
if (sDestFolderName == "")
oDestFolder = oDestList.RootFolder;
else
{
foreach (SPListItem oTmpFolder in oDestList.Folders)
{
if (oTmpFolder.Folder.Url == sDestFolderName)
{
oDestFolder = oTmpFolder.Folder;
break;
}
}
}
if (oDestFolder == null)
{
Console.WriteLine($"Cannot find destination folder '{sDestFolderName}'");
return;
}
// copy files
foreach (SPFile oFile in oFolder.Files)
{
Console.WriteLine($"FILE {oFile.Name}");
try
{
// add modifying user to destination web
oUser=oDestWeb.EnsureUser(
(string)oFile.Properties["vti_modifiedby"]
);
}
catch (Exception)
{
// add failed, use me as modifying user
oUser = oDestWeb.CurrentUser;
}
oFileCopy = oDestFolder.Files.Add(
$"{oDestWeb.Url}/{oDestFolder.Url}/{oFile.Name}",
oFile.OpenBinaryStream(),
oFile.Properties,
oUser,
oUser,
(DateTime)oFile.Properties["vti_timelastmodified"],
(DateTime)oFile.Properties["vti_timelastmodified"],
oFile.CheckInComment,
true);
}
// copy folders
foreach (SPFolder oSubFolder in oFolder.SubFolders)
{
Console.WriteLine($"FOLDER {oSubFolder.Url}");
try
{
// add modifying user to destination web
oUser = oDestWeb.EnsureUser(
(string)oSubFolder.Properties["vti_modifiedby"]
);
}
catch (Exception)
{
// add failed, use me as modifying user
oUser = oDestWeb.CurrentUser;
}
// first create on destination list
oNewDestFolder = oDestFolder.SubFolders.Add(oSubFolder.Name);
// set author/editor and timestamps
SPListItem oTmp = oDestList.GetItemByUniqueId(oNewDestFolder.UniqueId);
oTmp["Author"] = oUser;
oTmp["Editor"] = oUser;
oTmp["Created"] = oSubFolder.Properties["vti_nexttolasttimemodified"];
oTmp["Modified"] = oSubFolder.Properties["vti_nexttolasttimemodified"];
oTmp.Update();
// call ourself recursively
CopyFilesAndSubFolders(oLog, oSubFolder, oSourceWeb,
sDestSiteName, sDestWebName,
sDestListName, oNewDestFolder.Url);
}
// cleanup objects
oDestWeb.Dispose();
oDestSite.Dispose();
}
}
}
Die Reference Assembly bekommt man über das Nuget Package “Microsoft.SharePoint”, laufen lassen kann man das Ding dann allerdings nur auf einem SharePoint Server – mit Remote Debugger lässts sich dann aber bequem testen/debuggen.