Пример #1
0
        public static XDoc RestoreDeletedPage(uint pageid, Title newRootPath, string revertReason)
        {
            //Retrieve initial revisions of pages to restore
            //First item in the list is the page that initiated the transaction.
            //Talk pages are included.
            uint initialDeleteTranId             = 0;
            IList <ArchiveBE> pagesToRestore     = DbUtils.CurrentSession.Archive_GetPagesInTransaction(pageid, out initialDeleteTranId);
            TransactionBE     initialDeleteTrans = DbUtils.CurrentSession.Transactions_GetById(initialDeleteTranId);

            //Validate deleted page + transaction
            if (pagesToRestore.Count == 0)
            {
                throw new PageArchiveLogicNotFoundException(pageid);
            }

            if (initialDeleteTrans == null)
            {
                throw new PageArchiveBadTransactionFatalException(initialDeleteTranId, pageid);
            }

            //TODO MaxM: move the above gathering of what pages to restore to another method. Make this private.

            //Look for title conflicts
            List <Title> titles           = new List <Title>();
            List <ulong> pageidsToRestore = new List <ulong>();
            Dictionary <ulong, PageBE> restoredPagesById = null;
            DateTime utcTimestamp = DateTime.UtcNow;

            foreach (ArchiveBE p in pagesToRestore)
            {
                Title t = p.Title;
                if (newRootPath != null)
                {
                    t = BuildNewTitlesForMovedPage(pagesToRestore[0].Title, p.Title, newRootPath);
                }
                titles.Add(t);

                pageidsToRestore.Add(p.LastPageId);
            }

            IList <PageBE> currentPages = DbUtils.CurrentSession.Pages_GetByTitles(titles.ToArray());

            if (currentPages.Count > 0)
            {
                //Remove all conflicting redirect pages from target of restore if all conflicting pages are redirects
                List <PageBE> conflictingRedirects = new List <PageBE>();
                foreach (PageBE p in currentPages)
                {
                    if (p.IsRedirect)
                    {
                        conflictingRedirects.Add(p);
                    }
                }

                if (currentPages.Count == conflictingRedirects.Count && conflictingRedirects.Count > 0)
                {
                    //Remove existing redirects and refresh the conflicting pages list
                    PageBL.DeletePages(conflictingRedirects.ToArray(), utcTimestamp, 0, false);
                    currentPages = DbUtils.CurrentSession.Pages_GetByTitles(titles.ToArray());
                }
            }

            if (currentPages.Count > 0)
            {
                //return the name(s) of the conflicting page title(s)
                StringBuilder conflictTitles = new StringBuilder();
                foreach (PageBE p in currentPages)
                {
                    if (conflictTitles.Length > 0)
                    {
                        conflictTitles.Append(", ");
                    }

                    conflictTitles.Append(p.Title.AsPrefixedUserFriendlyPath());
                }

                throw new PageArchiveRestoreNamedPageConflictException(conflictTitles.ToString());
            }

            //Gather revisions for all pages to be restored.
            //Revisions are sorted by timestamp: oldest first.
            Dictionary <ulong, IList <ArchiveBE> > revisionsByPageId = DbUtils.CurrentSession.Archive_GetRevisionsByPageIds(pageidsToRestore);

            uint restoredPageTranId = 0;

            try {
                TransactionBE newTrans = new TransactionBE();
                newTrans.UserId    = DekiContext.Current.User.ID;
                newTrans.PageId    = pageid;
                newTrans.Title     = pagesToRestore[0].Title;
                newTrans.Type      = RC.PAGERESTORED;
                newTrans.TimeStamp = DateTime.UtcNow;
                restoredPageTranId = DbUtils.CurrentSession.Transactions_Insert(newTrans);

                //Pages must be restored in correct order (alphabetical ensures parent pages are restored before children).
                //pagesToRestore must be in alphabetical title order

                bool minorChange = false;
                foreach (ArchiveBE pageToRestore in pagesToRestore)
                {
                    IList <ArchiveBE> revisions = null;
                    if (revisionsByPageId.TryGetValue(pageToRestore.LastPageId, out revisions))
                    {
                        //Optionally restore page to different title
                        Title restoreToTitle = pageToRestore.Title;
                        if (newRootPath != null)
                        {
                            restoreToTitle = BuildNewTitlesForMovedPage(pagesToRestore[0].Title, pageToRestore.Title, newRootPath);
                        }

                        RestorePageRevisionsForPage(revisions.ToArray(), restoreToTitle, restoredPageTranId, minorChange, utcTimestamp);
                        DbUtils.CurrentSession.Archive_Delete(revisions.Select(e => e.Id).ToList());
                    }
                    minorChange = true;
                }

                //Retrieve the restored pages
                restoredPagesById = PageBL.GetPagesByIdsPreserveOrder(pageidsToRestore).AsHash(e => e.ID);

                // Restore attachments
                IList <ResourceBE> attachmentsToRestore = ResourceBL.Instance.GetResourcesByChangeSet(initialDeleteTrans.Id, ResourceBE.Type.FILE);
                foreach (ResourceBE at in attachmentsToRestore)
                {
                    PageBE restoredPage;
                    if (restoredPagesById.TryGetValue(at.ParentPageId.Value, out restoredPage))
                    {
                        AttachmentBL.Instance.RestoreAttachment(at, restoredPage, utcTimestamp, restoredPageTranId);
                    }
                }

                //Update the old transaction as reverted
                initialDeleteTrans.Reverted        = true;
                initialDeleteTrans.RevertTimeStamp = utcTimestamp;
                initialDeleteTrans.RevertUserId    = DekiContext.Current.User.ID;
                initialDeleteTrans.RevertReason    = revertReason;
                DbUtils.CurrentSession.Transactions_Update(initialDeleteTrans);
            } catch (Exception) {
                DbUtils.CurrentSession.Transactions_Delete(restoredPageTranId);
                throw;
            }

            //Build restore summary
            XDoc ret = new XDoc("pages.restored");

            foreach (ulong restoredPageId in pageidsToRestore)
            {
                PageBE restoredPage = null;
                if (restoredPagesById.TryGetValue((uint)restoredPageId, out restoredPage))
                {
                    ret.Add(PageBL.GetPageXml(restoredPage, string.Empty));
                }
            }

            return(ret);
        }