In overview the strategy is one of replaying history. Each version of the file is reposted to the document library. The comment and last modified date is handled with a combination of SPFile.Checkin() and SPListItem.Update(). Intrigued? Then read on….
The following code starts by dealing with the document versions. This collection cannot be relied upon to be in version order so the first step is to sort it. Each sorted version is then passed to a method that will add it to the destination library. The process is simply re-writing history -
// Start by sorting the versions in to version order then // iterate the sorted list of version adding them to the target // document library sourceVersions.Cast().OrderBy(f => f.VersionLabel).ToList().ForEach(version => { // Ensure the version modifier and creator exist at the destination and if not use the system account SPUser destCreator = null; try { destCreator = destWeb.EnsureUser(version.CreatedBy.LoginName); } catch (Exception) { // the user can not be found so assign the system account destCreator = destWeb.Site.SystemAccount; } //Ensure modifying user exists in the destination web SPUser destModifier = null; try { destModifier = destWeb.EnsureUser(version.CreatedBy.ToString()); } catch (Exception) { // the user can not be found so assign the system account destModifier = destWeb.Site.SystemAccount; } DateTime versionCreatedOn = version.File.TimeCreated.ToLocalTime(); DateTime versionModifiedOn = version.Created.ToLocalTime(); // Move the version to the destination library MoveDocument(sourceFile.Name, version.VersionLabel, destFolder, version.OpenBinary(), version.CheckInComment, createdOn, destCreator, modifiedOn, destModifier, destList); } // finally add code to add the current version of the file to the destination. // this is a duplicate of the procedure shown above for the file versions.
The code for adding a document to the destination library will need to switch off various versioning flags used by the library. This is not ideal but is the only means available in the SP Object Model that allows the version comment and last modified date to be maintained during the move.
private SPFile MoveDocument(string fileName, string versionLabel, SPFolder destFolder, byte[] content, string comment, DateTime createdOn, SPUser createdBy, DateTime modifiedOn, SPUser modifiedBy, SPList destList) { SPSite site = destFolder.ParentWeb.Site; SPWeb web = destFolder.ParentWeb; bool minorEnabled = destList.EnableMinorVersions; //Remove target if it exists SPFile file = null; file = web.GetFile(SPUtility.GetFullUrl(site, destFolder.ServerRelativeUrl + "/" + fileName)); if (!file.Exists) file = AddFileToFolder(fileName, destFolder, new byte[0]); FileVersion srcVersion = new FileVersion(versionLabel); FileVersion destVersion = new FileVersion(file.UIVersionLabel); //Increment the major version number int inc = srcVersion.Major - destVersion.Major - 1 + (srcVersion.Minor > 0 ? 1 : 0); for (int i = 0; i < inc; i++) { if (file.CheckedOutBy == null) file.CheckOut(); file = AddFileToFolder(fileName, destFolder, new byte[0]); file.CheckIn("Temp", SPCheckinType.MajorCheckIn); } //Increment minor version number if (minorEnabled) { inc = srcVersion.Minor - destVersion.Minor - 1; for (int i = 0; i < inc; i++) { if (file.CheckedOutBy == null) file.CheckOut(); file = AddFileToFolder(fileName, destFolder, new byte[0]); file.CheckIn("Temp", SPCheckinType.MinorCheckIn); } } //Add file content if (file.CheckedOutBy == null) file.CheckOut(); using (new MemoryStream(content)) file = AddFileToFolder(fileName, destFolder, content); //update file item metadata here if (action != null) action(file); file.Item.Update(); //Final check in if (srcVersion.Minor != 0 || !minorEnabled) { CheckInFile(file, comment, SPCheckinType.MinorCheckIn, modifiedBy, versionLabel); ((SPItem)file.Item)["Modified_x0020_By"] = (object)modifiedBy; ((SPItem)file.Item)["Created_x0020_By"] = (object)createdBy; ((SPItem)file.Item)["Modified"] = (object)modifiedOn; ((SPItem)file.Item)["Created"] = (object)createdOn; file.Item.UpdateOverwriteVersion(); } else { CheckInFile(file, comment, SPCheckinType.MajorCheckIn, modifiedBy, versionLabel); // unpublish the major version file.TakeOffline(); // check out the file so that it can be edited file.CheckOut(); // switch off versioning so that a minor version is not created. // With versioning on it will be the minor version that receives // the modification history which will defeat the object of this process. bool versioningEnabled = destList.EnableVersioning; destList.EnableVersioning = false; destList.Update(); try { // check in the file. Because versioning is now off the major version receives the changes CheckInFile(file, comment, SPCheckinType.MajorCheckIn, modifiedBy, versionLabel); // Updated the file's associated list item file.Item["Modified_x0020_By"] = (object)modifiedBy; file.Item["Created_x0020_By"] = (object)createdBy; file.Item["Modified"] = (object)modifiedOn; file.Item["Created"] = (object)createdOn; file.Item.UpdateOverwriteVersion(); } finally // if it all goes pear shaped or succeeds re-establish versioning state { destList.EnableVersioning = versioningEnabled; destList.EnableMinorVersions = minorEnabled; destList.Update(); } } //remove temp versions int tempVerCount = file.Versions.Count; for (int i = tempVerCount - 1; i >= 0; i--) { if (file.Versions[i].Size == 0) { try { file.Versions.Delete(i); } catch (SPException e) { if (e.ErrorCode == -2147024710) //Cannot delete current version. So force deletion { file.TakeOffline(); file.Versions.Delete(i); } else throw; } } } return file; }