Maintain SPFile version comment and last modified date when moving documents

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;
        }
This entry was posted in SharePoint, SharePoint 2007, SharePoint 2010 and tagged , , . Bookmark the permalink.

Comments are closed.