MediaWiki
REL1_19
|
00001 <?php 00029 class PageArchive { 00030 00034 protected $title; 00035 var $fileStatus; 00036 00037 function __construct( $title ) { 00038 if( is_null( $title ) ) { 00039 throw new MWException( __METHOD__ . ' given a null title.' ); 00040 } 00041 $this->title = $title; 00042 } 00043 00051 public static function listAllPages() { 00052 $dbr = wfGetDB( DB_SLAVE ); 00053 return self::listPages( $dbr, '' ); 00054 } 00055 00064 public static function listPagesByPrefix( $prefix ) { 00065 $dbr = wfGetDB( DB_SLAVE ); 00066 00067 $title = Title::newFromText( $prefix ); 00068 if( $title ) { 00069 $ns = $title->getNamespace(); 00070 $prefix = $title->getDBkey(); 00071 } else { 00072 // Prolly won't work too good 00073 // @todo handle bare namespace names cleanly? 00074 $ns = 0; 00075 } 00076 $conds = array( 00077 'ar_namespace' => $ns, 00078 'ar_title' . $dbr->buildLike( $prefix, $dbr->anyString() ), 00079 ); 00080 return self::listPages( $dbr, $conds ); 00081 } 00082 00088 protected static function listPages( $dbr, $condition ) { 00089 return $dbr->resultObject( 00090 $dbr->select( 00091 array( 'archive' ), 00092 array( 00093 'ar_namespace', 00094 'ar_title', 00095 'COUNT(*) AS count' 00096 ), 00097 $condition, 00098 __METHOD__, 00099 array( 00100 'GROUP BY' => 'ar_namespace,ar_title', 00101 'ORDER BY' => 'ar_namespace,ar_title', 00102 'LIMIT' => 100, 00103 ) 00104 ) 00105 ); 00106 } 00107 00114 function listRevisions() { 00115 $dbr = wfGetDB( DB_SLAVE ); 00116 $res = $dbr->select( 'archive', 00117 array( 00118 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', 00119 'ar_comment', 'ar_len', 'ar_deleted', 'ar_rev_id', 'ar_sha1' 00120 ), 00121 array( 'ar_namespace' => $this->title->getNamespace(), 00122 'ar_title' => $this->title->getDBkey() ), 00123 'PageArchive::listRevisions', 00124 array( 'ORDER BY' => 'ar_timestamp DESC' ) ); 00125 $ret = $dbr->resultObject( $res ); 00126 return $ret; 00127 } 00128 00137 function listFiles() { 00138 if( $this->title->getNamespace() == NS_FILE ) { 00139 $dbr = wfGetDB( DB_SLAVE ); 00140 $res = $dbr->select( 'filearchive', 00141 array( 00142 'fa_id', 00143 'fa_name', 00144 'fa_archive_name', 00145 'fa_storage_key', 00146 'fa_storage_group', 00147 'fa_size', 00148 'fa_width', 00149 'fa_height', 00150 'fa_bits', 00151 'fa_metadata', 00152 'fa_media_type', 00153 'fa_major_mime', 00154 'fa_minor_mime', 00155 'fa_description', 00156 'fa_user', 00157 'fa_user_text', 00158 'fa_timestamp', 00159 'fa_deleted' ), 00160 array( 'fa_name' => $this->title->getDBkey() ), 00161 __METHOD__, 00162 array( 'ORDER BY' => 'fa_timestamp DESC' ) ); 00163 $ret = $dbr->resultObject( $res ); 00164 return $ret; 00165 } 00166 return null; 00167 } 00168 00176 function getRevision( $timestamp ) { 00177 $dbr = wfGetDB( DB_SLAVE ); 00178 $row = $dbr->selectRow( 'archive', 00179 array( 00180 'ar_rev_id', 00181 'ar_text', 00182 'ar_comment', 00183 'ar_user', 00184 'ar_user_text', 00185 'ar_timestamp', 00186 'ar_minor_edit', 00187 'ar_flags', 00188 'ar_text_id', 00189 'ar_deleted', 00190 'ar_len', 00191 'ar_sha1', 00192 ), 00193 array( 'ar_namespace' => $this->title->getNamespace(), 00194 'ar_title' => $this->title->getDBkey(), 00195 'ar_timestamp' => $dbr->timestamp( $timestamp ) ), 00196 __METHOD__ ); 00197 if( $row ) { 00198 return Revision::newFromArchiveRow( $row, array( 'page' => $this->title->getArticleId() ) ); 00199 } else { 00200 return null; 00201 } 00202 } 00203 00214 function getPreviousRevision( $timestamp ) { 00215 $dbr = wfGetDB( DB_SLAVE ); 00216 00217 // Check the previous deleted revision... 00218 $row = $dbr->selectRow( 'archive', 00219 'ar_timestamp', 00220 array( 'ar_namespace' => $this->title->getNamespace(), 00221 'ar_title' => $this->title->getDBkey(), 00222 'ar_timestamp < ' . 00223 $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ), 00224 __METHOD__, 00225 array( 00226 'ORDER BY' => 'ar_timestamp DESC', 00227 'LIMIT' => 1 ) ); 00228 $prevDeleted = $row ? wfTimestamp( TS_MW, $row->ar_timestamp ) : false; 00229 00230 $row = $dbr->selectRow( array( 'page', 'revision' ), 00231 array( 'rev_id', 'rev_timestamp' ), 00232 array( 00233 'page_namespace' => $this->title->getNamespace(), 00234 'page_title' => $this->title->getDBkey(), 00235 'page_id = rev_page', 00236 'rev_timestamp < ' . 00237 $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ), 00238 __METHOD__, 00239 array( 00240 'ORDER BY' => 'rev_timestamp DESC', 00241 'LIMIT' => 1 ) ); 00242 $prevLive = $row ? wfTimestamp( TS_MW, $row->rev_timestamp ) : false; 00243 $prevLiveId = $row ? intval( $row->rev_id ) : null; 00244 00245 if( $prevLive && $prevLive > $prevDeleted ) { 00246 // Most prior revision was live 00247 return Revision::newFromId( $prevLiveId ); 00248 } elseif( $prevDeleted ) { 00249 // Most prior revision was deleted 00250 return $this->getRevision( $prevDeleted ); 00251 } else { 00252 // No prior revision on this page. 00253 return null; 00254 } 00255 } 00256 00263 function getTextFromRow( $row ) { 00264 if( is_null( $row->ar_text_id ) ) { 00265 // An old row from MediaWiki 1.4 or previous. 00266 // Text is embedded in this row in classic compression format. 00267 return Revision::getRevisionText( $row, 'ar_' ); 00268 } else { 00269 // New-style: keyed to the text storage backend. 00270 $dbr = wfGetDB( DB_SLAVE ); 00271 $text = $dbr->selectRow( 'text', 00272 array( 'old_text', 'old_flags' ), 00273 array( 'old_id' => $row->ar_text_id ), 00274 __METHOD__ ); 00275 return Revision::getRevisionText( $text ); 00276 } 00277 } 00278 00287 function getLastRevisionText() { 00288 $dbr = wfGetDB( DB_SLAVE ); 00289 $row = $dbr->selectRow( 'archive', 00290 array( 'ar_text', 'ar_flags', 'ar_text_id' ), 00291 array( 'ar_namespace' => $this->title->getNamespace(), 00292 'ar_title' => $this->title->getDBkey() ), 00293 __METHOD__, 00294 array( 'ORDER BY' => 'ar_timestamp DESC' ) ); 00295 if( $row ) { 00296 return $this->getTextFromRow( $row ); 00297 } else { 00298 return null; 00299 } 00300 } 00301 00307 function isDeleted() { 00308 $dbr = wfGetDB( DB_SLAVE ); 00309 $n = $dbr->selectField( 'archive', 'COUNT(ar_title)', 00310 array( 'ar_namespace' => $this->title->getNamespace(), 00311 'ar_title' => $this->title->getDBkey() ) ); 00312 return ( $n > 0 ); 00313 } 00314 00328 function undelete( $timestamps, $comment = '', $fileVersions = array(), $unsuppress = false ) { 00329 // If both the set of text revisions and file revisions are empty, 00330 // restore everything. Otherwise, just restore the requested items. 00331 $restoreAll = empty( $timestamps ) && empty( $fileVersions ); 00332 00333 $restoreText = $restoreAll || !empty( $timestamps ); 00334 $restoreFiles = $restoreAll || !empty( $fileVersions ); 00335 00336 if( $restoreFiles && $this->title->getNamespace() == NS_FILE ) { 00337 $img = wfLocalFile( $this->title ); 00338 $this->fileStatus = $img->restore( $fileVersions, $unsuppress ); 00339 if ( !$this->fileStatus->isOk() ) { 00340 return false; 00341 } 00342 $filesRestored = $this->fileStatus->successCount; 00343 } else { 00344 $filesRestored = 0; 00345 } 00346 00347 if( $restoreText ) { 00348 $textRestored = $this->undeleteRevisions( $timestamps, $unsuppress, $comment ); 00349 if( $textRestored === false ) { // It must be one of UNDELETE_* 00350 return false; 00351 } 00352 } else { 00353 $textRestored = 0; 00354 } 00355 00356 // Touch the log! 00357 global $wgContLang; 00358 $log = new LogPage( 'delete' ); 00359 00360 if( $textRestored && $filesRestored ) { 00361 $reason = wfMsgExt( 'undeletedrevisions-files', array( 'content', 'parsemag' ), 00362 $wgContLang->formatNum( $textRestored ), 00363 $wgContLang->formatNum( $filesRestored ) ); 00364 } elseif( $textRestored ) { 00365 $reason = wfMsgExt( 'undeletedrevisions', array( 'content', 'parsemag' ), 00366 $wgContLang->formatNum( $textRestored ) ); 00367 } elseif( $filesRestored ) { 00368 $reason = wfMsgExt( 'undeletedfiles', array( 'content', 'parsemag' ), 00369 $wgContLang->formatNum( $filesRestored ) ); 00370 } else { 00371 wfDebug( "Undelete: nothing undeleted...\n" ); 00372 return false; 00373 } 00374 00375 if( trim( $comment ) != '' ) { 00376 $reason .= wfMsgForContent( 'colon-separator' ) . $comment; 00377 } 00378 $log->addEntry( 'restore', $this->title, $reason ); 00379 00380 return array( $textRestored, $filesRestored, $reason ); 00381 } 00382 00394 private function undeleteRevisions( $timestamps, $unsuppress = false, $comment = '' ) { 00395 if ( wfReadOnly() ) { 00396 return false; 00397 } 00398 $restoreAll = empty( $timestamps ); 00399 00400 $dbw = wfGetDB( DB_MASTER ); 00401 00402 # Does this page already exist? We'll have to update it... 00403 $article = WikiPage::factory( $this->title ); 00404 # Load latest data for the current page (bug 31179) 00405 $article->loadPageData( 'fromdbmaster' ); 00406 $oldcountable = $article->isCountable(); 00407 00408 $page = $dbw->selectRow( 'page', 00409 array( 'page_id', 'page_latest' ), 00410 array( 'page_namespace' => $this->title->getNamespace(), 00411 'page_title' => $this->title->getDBkey() ), 00412 __METHOD__, 00413 array( 'FOR UPDATE' ) // lock page 00414 ); 00415 if( $page ) { 00416 $makepage = false; 00417 # Page already exists. Import the history, and if necessary 00418 # we'll update the latest revision field in the record. 00419 $newid = 0; 00420 $pageId = $page->page_id; 00421 $previousRevId = $page->page_latest; 00422 # Get the time span of this page 00423 $previousTimestamp = $dbw->selectField( 'revision', 'rev_timestamp', 00424 array( 'rev_id' => $previousRevId ), 00425 __METHOD__ ); 00426 if( $previousTimestamp === false ) { 00427 wfDebug( __METHOD__.": existing page refers to a page_latest that does not exist\n" ); 00428 return 0; 00429 } 00430 } else { 00431 # Have to create a new article... 00432 $makepage = true; 00433 $previousRevId = 0; 00434 $previousTimestamp = 0; 00435 } 00436 00437 if( $restoreAll ) { 00438 $oldones = '1 = 1'; # All revisions... 00439 } else { 00440 $oldts = implode( ',', 00441 array_map( array( &$dbw, 'addQuotes' ), 00442 array_map( array( &$dbw, 'timestamp' ), 00443 $timestamps ) ) ); 00444 00445 $oldones = "ar_timestamp IN ( {$oldts} )"; 00446 } 00447 00451 $result = $dbw->select( 'archive', 00452 /* fields */ array( 00453 'ar_rev_id', 00454 'ar_text', 00455 'ar_comment', 00456 'ar_user', 00457 'ar_user_text', 00458 'ar_timestamp', 00459 'ar_minor_edit', 00460 'ar_flags', 00461 'ar_text_id', 00462 'ar_deleted', 00463 'ar_page_id', 00464 'ar_len', 00465 'ar_sha1' ), 00466 /* WHERE */ array( 00467 'ar_namespace' => $this->title->getNamespace(), 00468 'ar_title' => $this->title->getDBkey(), 00469 $oldones ), 00470 __METHOD__, 00471 /* options */ array( 'ORDER BY' => 'ar_timestamp' ) 00472 ); 00473 $ret = $dbw->resultObject( $result ); 00474 $rev_count = $dbw->numRows( $result ); 00475 if( !$rev_count ) { 00476 wfDebug( __METHOD__ . ": no revisions to restore\n" ); 00477 return false; // ??? 00478 } 00479 00480 $ret->seek( $rev_count - 1 ); // move to last 00481 $row = $ret->fetchObject(); // get newest archived rev 00482 $ret->seek( 0 ); // move back 00483 00484 if( $makepage ) { 00485 // Check the state of the newest to-be version... 00486 if( !$unsuppress && ( $row->ar_deleted & Revision::DELETED_TEXT ) ) { 00487 return false; // we can't leave the current revision like this! 00488 } 00489 // Safe to insert now... 00490 $newid = $article->insertOn( $dbw ); 00491 $pageId = $newid; 00492 } else { 00493 // Check if a deleted revision will become the current revision... 00494 if( $row->ar_timestamp > $previousTimestamp ) { 00495 // Check the state of the newest to-be version... 00496 if( !$unsuppress && ( $row->ar_deleted & Revision::DELETED_TEXT ) ) { 00497 return false; // we can't leave the current revision like this! 00498 } 00499 } 00500 } 00501 00502 $revision = null; 00503 $restored = 0; 00504 00505 foreach ( $ret as $row ) { 00506 // Check for key dupes due to shitty archive integrity. 00507 if( $row->ar_rev_id ) { 00508 $exists = $dbw->selectField( 'revision', '1', 00509 array( 'rev_id' => $row->ar_rev_id ), __METHOD__ ); 00510 if( $exists ) { 00511 continue; // don't throw DB errors 00512 } 00513 } 00514 // Insert one revision at a time...maintaining deletion status 00515 // unless we are specifically removing all restrictions... 00516 $revision = Revision::newFromArchiveRow( $row, 00517 array( 00518 'page' => $pageId, 00519 'deleted' => $unsuppress ? 0 : $row->ar_deleted 00520 ) ); 00521 00522 $revision->insertOn( $dbw ); 00523 $restored++; 00524 00525 wfRunHooks( 'ArticleRevisionUndeleted', array( &$this->title, $revision, $row->ar_page_id ) ); 00526 } 00527 # Now that it's safely stored, take it out of the archive 00528 $dbw->delete( 'archive', 00529 /* WHERE */ array( 00530 'ar_namespace' => $this->title->getNamespace(), 00531 'ar_title' => $this->title->getDBkey(), 00532 $oldones ), 00533 __METHOD__ ); 00534 00535 // Was anything restored at all? 00536 if ( $restored == 0 ) { 00537 return 0; 00538 } 00539 00540 $created = (bool)$newid; 00541 00542 // Attach the latest revision to the page... 00543 $wasnew = $article->updateIfNewerOn( $dbw, $revision, $previousRevId ); 00544 if ( $created || $wasnew ) { 00545 // Update site stats, link tables, etc 00546 $user = User::newFromName( $revision->getRawUserText(), false ); 00547 $article->doEditUpdates( $revision, $user, array( 'created' => $created, 'oldcountable' => $oldcountable ) ); 00548 } 00549 00550 wfRunHooks( 'ArticleUndelete', array( &$this->title, $created, $comment ) ); 00551 00552 if( $this->title->getNamespace() == NS_FILE ) { 00553 $update = new HTMLCacheUpdate( $this->title, 'imagelinks' ); 00554 $update->doUpdate(); 00555 } 00556 00557 return $restored; 00558 } 00559 00563 function getFileStatus() { return $this->fileStatus; } 00564 } 00565 00572 class SpecialUndelete extends SpecialPage { 00573 var $mAction, $mTarget, $mTimestamp, $mRestore, $mInvert, $mFilename; 00574 var $mTargetTimestamp, $mAllowed, $mCanView, $mComment, $mToken; 00575 00579 var $mTargetObj; 00580 00581 function __construct() { 00582 parent::__construct( 'Undelete', 'deletedhistory' ); 00583 } 00584 00585 function loadRequest( $par ) { 00586 $request = $this->getRequest(); 00587 $user = $this->getUser(); 00588 00589 $this->mAction = $request->getVal( 'action' ); 00590 if ( $par !== null && $par !== '' ) { 00591 $this->mTarget = $par; 00592 } else { 00593 $this->mTarget = $request->getVal( 'target' ); 00594 } 00595 $this->mTargetObj = null; 00596 if ( $this->mTarget !== null && $this->mTarget !== '' ) { 00597 $this->mTargetObj = Title::newFromURL( $this->mTarget ); 00598 } 00599 $this->mSearchPrefix = $request->getText( 'prefix' ); 00600 $time = $request->getVal( 'timestamp' ); 00601 $this->mTimestamp = $time ? wfTimestamp( TS_MW, $time ) : ''; 00602 $this->mFilename = $request->getVal( 'file' ); 00603 00604 $posted = $request->wasPosted() && 00605 $user->matchEditToken( $request->getVal( 'wpEditToken' ) ); 00606 $this->mRestore = $request->getCheck( 'restore' ) && $posted; 00607 $this->mInvert = $request->getCheck( 'invert' ) && $posted; 00608 $this->mPreview = $request->getCheck( 'preview' ) && $posted; 00609 $this->mDiff = $request->getCheck( 'diff' ); 00610 $this->mDiffOnly = $request->getBool( 'diffonly', $this->getUser()->getOption( 'diffonly' ) ); 00611 $this->mComment = $request->getText( 'wpComment' ); 00612 $this->mUnsuppress = $request->getVal( 'wpUnsuppress' ) && $user->isAllowed( 'suppressrevision' ); 00613 $this->mToken = $request->getVal( 'token' ); 00614 00615 if ( $user->isAllowed( 'undelete' ) && !$user->isBlocked() ) { 00616 $this->mAllowed = true; // user can restore 00617 $this->mCanView = true; // user can view content 00618 } elseif ( $user->isAllowed( 'deletedtext' ) ) { 00619 $this->mAllowed = false; // user cannot restore 00620 $this->mCanView = true; // user can view content 00621 $this->mRestore = false; 00622 } else { // user can only view the list of revisions 00623 $this->mAllowed = false; 00624 $this->mCanView = false; 00625 $this->mTimestamp = ''; 00626 $this->mRestore = false; 00627 } 00628 00629 if( $this->mRestore || $this->mInvert ) { 00630 $timestamps = array(); 00631 $this->mFileVersions = array(); 00632 foreach( $request->getValues() as $key => $val ) { 00633 $matches = array(); 00634 if( preg_match( '/^ts(\d{14})$/', $key, $matches ) ) { 00635 array_push( $timestamps, $matches[1] ); 00636 } 00637 00638 if( preg_match( '/^fileid(\d+)$/', $key, $matches ) ) { 00639 $this->mFileVersions[] = intval( $matches[1] ); 00640 } 00641 } 00642 rsort( $timestamps ); 00643 $this->mTargetTimestamp = $timestamps; 00644 } 00645 } 00646 00647 function execute( $par ) { 00648 $this->checkPermissions(); 00649 $user = $this->getUser(); 00650 00651 $this->setHeaders(); 00652 $this->outputHeader(); 00653 00654 $this->loadRequest( $par ); 00655 00656 $out = $this->getOutput(); 00657 00658 if ( is_null( $this->mTargetObj ) ) { 00659 $out->addWikiMsg( 'undelete-header' ); 00660 00661 # Not all users can just browse every deleted page from the list 00662 if ( $user->isAllowed( 'browsearchive' ) ) { 00663 $this->showSearchForm(); 00664 } 00665 return; 00666 } 00667 00668 if ( $this->mAllowed ) { 00669 $out->setPageTitle( $this->msg( 'undeletepage' ) ); 00670 } else { 00671 $out->setPageTitle( $this->msg( 'viewdeletedpage' ) ); 00672 } 00673 00674 $this->getSkin()->setRelevantTitle( $this->mTargetObj ); 00675 00676 if ( $this->mTimestamp !== '' ) { 00677 $this->showRevision( $this->mTimestamp ); 00678 } elseif ( $this->mFilename !== null ) { 00679 $file = new ArchivedFile( $this->mTargetObj, '', $this->mFilename ); 00680 // Check if user is allowed to see this file 00681 if ( !$file->exists() ) { 00682 $out->addWikiMsg( 'filedelete-nofile', $this->mFilename ); 00683 } elseif ( !$file->userCan( File::DELETED_FILE, $user ) ) { 00684 if( $file->isDeleted( File::DELETED_RESTRICTED ) ) { 00685 throw new PermissionsError( 'suppressrevision' ); 00686 } else { 00687 throw new PermissionsError( 'deletedtext' ); 00688 } 00689 } elseif ( !$user->matchEditToken( $this->mToken, $this->mFilename ) ) { 00690 $this->showFileConfirmationForm( $this->mFilename ); 00691 } else { 00692 $this->showFile( $this->mFilename ); 00693 } 00694 } elseif ( $this->mRestore && $this->mAction == 'submit' ) { 00695 $this->undelete(); 00696 } else { 00697 $this->showHistory(); 00698 } 00699 } 00700 00701 function showSearchForm() { 00702 global $wgScript; 00703 00704 $out = $this->getOutput(); 00705 $out->setPageTitle( $this->msg( 'undelete-search-title' ) ); 00706 $out->addHTML( 00707 Xml::openElement( 'form', array( 00708 'method' => 'get', 00709 'action' => $wgScript ) ) . 00710 Xml::fieldset( $this->msg( 'undelete-search-box' )->text() ) . 00711 Html::hidden( 'title', 00712 $this->getTitle()->getPrefixedDbKey() ) . 00713 Xml::inputLabel( $this->msg( 'undelete-search-prefix' )->text(), 00714 'prefix', 'prefix', 20, 00715 $this->mSearchPrefix ) . ' ' . 00716 Xml::submitButton( $this->msg( 'undelete-search-submit' )->text() ) . 00717 Xml::closeElement( 'fieldset' ) . 00718 Xml::closeElement( 'form' ) 00719 ); 00720 00721 # List undeletable articles 00722 if( $this->mSearchPrefix ) { 00723 $result = PageArchive::listPagesByPrefix( $this->mSearchPrefix ); 00724 $this->showList( $result ); 00725 } 00726 } 00727 00734 private function showList( $result ) { 00735 $out = $this->getOutput(); 00736 00737 if( $result->numRows() == 0 ) { 00738 $out->addWikiMsg( 'undelete-no-results' ); 00739 return; 00740 } 00741 00742 $out->addWikiMsg( 'undeletepagetext', $this->getLanguage()->formatNum( $result->numRows() ) ); 00743 00744 $undelete = $this->getTitle(); 00745 $out->addHTML( "<ul>\n" ); 00746 foreach ( $result as $row ) { 00747 $title = Title::makeTitleSafe( $row->ar_namespace, $row->ar_title ); 00748 $link = Linker::linkKnown( 00749 $undelete, 00750 htmlspecialchars( $title->getPrefixedText() ), 00751 array(), 00752 array( 'target' => $title->getPrefixedText() ) 00753 ); 00754 $revs = $this->msg( 'undeleterevisions' )->numParams( $row->count )->parse(); 00755 $out->addHTML( "<li>{$link} ({$revs})</li>\n" ); 00756 } 00757 $result->free(); 00758 $out->addHTML( "</ul>\n" ); 00759 00760 return true; 00761 } 00762 00763 private function showRevision( $timestamp ) { 00764 if( !preg_match( '/[0-9]{14}/', $timestamp ) ) { 00765 return 0; 00766 } 00767 00768 $archive = new PageArchive( $this->mTargetObj ); 00769 wfRunHooks( 'UndeleteForm::showRevision', array( &$archive, $this->mTargetObj ) ); 00770 $rev = $archive->getRevision( $timestamp ); 00771 00772 $out = $this->getOutput(); 00773 $user = $this->getUser(); 00774 00775 if( !$rev ) { 00776 $out->addWikiMsg( 'undeleterevision-missing' ); 00777 return; 00778 } 00779 00780 if( $rev->isDeleted( Revision::DELETED_TEXT ) ) { 00781 if( !$rev->userCan( Revision::DELETED_TEXT, $user ) ) { 00782 $out->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", 'rev-deleted-text-permission' ); 00783 return; 00784 } else { 00785 $out->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", 'rev-deleted-text-view' ); 00786 $out->addHTML( '<br />' ); 00787 // and we are allowed to see... 00788 } 00789 } 00790 00791 if( $this->mDiff ) { 00792 $previousRev = $archive->getPreviousRevision( $timestamp ); 00793 if( $previousRev ) { 00794 $this->showDiff( $previousRev, $rev ); 00795 if( $this->mDiffOnly ) { 00796 return; 00797 } else { 00798 $out->addHTML( '<hr />' ); 00799 } 00800 } else { 00801 $out->addWikiMsg( 'undelete-nodiff' ); 00802 } 00803 } 00804 00805 $link = Linker::linkKnown( 00806 $this->getTitle( $this->mTargetObj->getPrefixedDBkey() ), 00807 htmlspecialchars( $this->mTargetObj->getPrefixedText() ) 00808 ); 00809 00810 $lang = $this->getLanguage(); 00811 00812 // date and time are separate parameters to facilitate localisation. 00813 // $time is kept for backward compat reasons. 00814 $time = $lang->userTimeAndDate( $timestamp, $user ); 00815 $d = $lang->userDate( $timestamp, $user ); 00816 $t = $lang->userTime( $timestamp, $user ); 00817 $userLink = Linker::revUserTools( $rev ); 00818 00819 if( $this->mPreview ) { 00820 $openDiv = '<div id="mw-undelete-revision" class="mw-warning">'; 00821 } else { 00822 $openDiv = '<div id="mw-undelete-revision">'; 00823 } 00824 $out->addHTML( $openDiv ); 00825 00826 // Revision delete links 00827 if ( !$this->mDiff ) { 00828 $revdel = Linker::getRevDeleteLink( $user, $rev, $this->mTargetObj ); 00829 if ( $revdel ) { 00830 $out->addHTML( "$revdel " ); 00831 } 00832 } 00833 00834 $out->addHTML( $this->msg( 'undelete-revision' )->rawParams( $link )->params( 00835 $time )->rawParams( $userLink )->params( $d, $t )->parse() . '</div>' ); 00836 wfRunHooks( 'UndeleteShowRevision', array( $this->mTargetObj, $rev ) ); 00837 00838 if( $this->mPreview ) { 00839 // Hide [edit]s 00840 $popts = $out->parserOptions(); 00841 $popts->setEditSection( false ); 00842 $out->parserOptions( $popts ); 00843 $out->addWikiTextTitleTidy( $rev->getText( Revision::FOR_THIS_USER, $user ), $this->mTargetObj, true ); 00844 } 00845 00846 $out->addHTML( 00847 Xml::element( 'textarea', array( 00848 'readonly' => 'readonly', 00849 'cols' => intval( $user->getOption( 'cols' ) ), 00850 'rows' => intval( $user->getOption( 'rows' ) ) ), 00851 $rev->getText( Revision::FOR_THIS_USER, $user ) . "\n" ) . 00852 Xml::openElement( 'div' ) . 00853 Xml::openElement( 'form', array( 00854 'method' => 'post', 00855 'action' => $this->getTitle()->getLocalURL( array( 'action' => 'submit' ) ) ) ) . 00856 Xml::element( 'input', array( 00857 'type' => 'hidden', 00858 'name' => 'target', 00859 'value' => $this->mTargetObj->getPrefixedDbKey() ) ) . 00860 Xml::element( 'input', array( 00861 'type' => 'hidden', 00862 'name' => 'timestamp', 00863 'value' => $timestamp ) ) . 00864 Xml::element( 'input', array( 00865 'type' => 'hidden', 00866 'name' => 'wpEditToken', 00867 'value' => $user->getEditToken() ) ) . 00868 Xml::element( 'input', array( 00869 'type' => 'submit', 00870 'name' => 'preview', 00871 'value' => $this->msg( 'showpreview' )->text() ) ) . 00872 Xml::element( 'input', array( 00873 'name' => 'diff', 00874 'type' => 'submit', 00875 'value' => $this->msg( 'showdiff' )->text() ) ) . 00876 Xml::closeElement( 'form' ) . 00877 Xml::closeElement( 'div' ) ); 00878 } 00879 00888 function showDiff( $previousRev, $currentRev ) { 00889 $diffEngine = new DifferenceEngine( $this->getContext() ); 00890 $diffEngine->showDiffStyle(); 00891 $this->getOutput()->addHTML( 00892 "<div>" . 00893 "<table border='0' width='98%' cellpadding='0' cellspacing='4' class='diff'>" . 00894 "<col class='diff-marker' />" . 00895 "<col class='diff-content' />" . 00896 "<col class='diff-marker' />" . 00897 "<col class='diff-content' />" . 00898 "<tr>" . 00899 "<td colspan='2' width='50%' align='center' class='diff-otitle'>" . 00900 $this->diffHeader( $previousRev, 'o' ) . 00901 "</td>\n" . 00902 "<td colspan='2' width='50%' align='center' class='diff-ntitle'>" . 00903 $this->diffHeader( $currentRev, 'n' ) . 00904 "</td>\n" . 00905 "</tr>" . 00906 $diffEngine->generateDiffBody( 00907 $previousRev->getText(), $currentRev->getText() ) . 00908 "</table>" . 00909 "</div>\n" 00910 ); 00911 } 00912 00918 private function diffHeader( $rev, $prefix ) { 00919 $isDeleted = !( $rev->getId() && $rev->getTitle() ); 00920 if( $isDeleted ) { 00922 $targetPage = $this->getTitle(); 00923 $targetQuery = array( 00924 'target' => $this->mTargetObj->getPrefixedText(), 00925 'timestamp' => wfTimestamp( TS_MW, $rev->getTimestamp() ) 00926 ); 00927 } else { 00929 $targetPage = $rev->getTitle(); 00930 $targetQuery = array( 'oldid' => $rev->getId() ); 00931 } 00932 // Add show/hide deletion links if available 00933 $user = $this->getUser(); 00934 $lang = $this->getLanguage(); 00935 $rdel = Linker::getRevDeleteLink( $user, $rev, $this->mTargetObj ); 00936 if ( $rdel ) $rdel = " $rdel"; 00937 return 00938 '<div id="mw-diff-' . $prefix . 'title1"><strong>' . 00939 Linker::link( 00940 $targetPage, 00941 $this->msg( 00942 'revisionasof', 00943 $lang->userTimeAndDate( $rev->getTimestamp(), $user ), 00944 $lang->userDate( $rev->getTimestamp(), $user ), 00945 $lang->userTime( $rev->getTimestamp(), $user ) 00946 )->escaped(), 00947 array(), 00948 $targetQuery 00949 ) . 00950 '</strong></div>' . 00951 '<div id="mw-diff-'.$prefix.'title2">' . 00952 Linker::revUserTools( $rev ) . '<br />' . 00953 '</div>' . 00954 '<div id="mw-diff-'.$prefix.'title3">' . 00955 Linker::revComment( $rev ) . $rdel . '<br />' . 00956 '</div>'; 00957 } 00958 00962 private function showFileConfirmationForm( $key ) { 00963 $out = $this->getOutput(); 00964 $lang = $this->getLanguage(); 00965 $user = $this->getUser(); 00966 $file = new ArchivedFile( $this->mTargetObj, '', $this->mFilename ); 00967 $out->addWikiMsg( 'undelete-show-file-confirm', 00968 $this->mTargetObj->getText(), 00969 $lang->userDate( $file->getTimestamp(), $user ), 00970 $lang->userTime( $file->getTimestamp(), $user ) ); 00971 $out->addHTML( 00972 Xml::openElement( 'form', array( 00973 'method' => 'POST', 00974 'action' => $this->getTitle()->getLocalURL( 00975 'target=' . urlencode( $this->mTarget ) . 00976 '&file=' . urlencode( $key ) . 00977 '&token=' . urlencode( $user->getEditToken( $key ) ) ) 00978 ) 00979 ) . 00980 Xml::submitButton( $this->msg( 'undelete-show-file-submit' )->text() ) . 00981 '</form>' 00982 ); 00983 } 00984 00988 private function showFile( $key ) { 00989 $this->getOutput()->disable(); 00990 00991 # We mustn't allow the output to be Squid cached, otherwise 00992 # if an admin previews a deleted image, and it's cached, then 00993 # a user without appropriate permissions can toddle off and 00994 # nab the image, and Squid will serve it 00995 $response = $this->getRequest()->response(); 00996 $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' ); 00997 $response->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' ); 00998 $response->header( 'Pragma: no-cache' ); 00999 01000 $repo = RepoGroup::singleton()->getLocalRepo(); 01001 $path = $repo->getZonePath( 'deleted' ) . '/' . $repo->getDeletedHashPath( $key ) . $key; 01002 $repo->streamFile( $path ); 01003 } 01004 01005 private function showHistory() { 01006 $out = $this->getOutput(); 01007 if( $this->mAllowed ) { 01008 $out->addModules( 'mediawiki.special.undelete' ); 01009 } 01010 $out->wrapWikiMsg( 01011 "<div class='mw-undelete-pagetitle'>\n$1\n</div>\n", 01012 array( 'undeletepagetitle', $this->mTargetObj->getPrefixedText() ) 01013 ); 01014 01015 $archive = new PageArchive( $this->mTargetObj ); 01016 wfRunHooks( 'UndeleteForm::showHistory', array( &$archive, $this->mTargetObj ) ); 01017 /* 01018 $text = $archive->getLastRevisionText(); 01019 if( is_null( $text ) ) { 01020 $out->addWikiMsg( 'nohistory' ); 01021 return; 01022 } 01023 */ 01024 $out->addHTML( '<div class="mw-undelete-history">' ); 01025 if ( $this->mAllowed ) { 01026 $out->addWikiMsg( 'undeletehistory' ); 01027 $out->addWikiMsg( 'undeleterevdel' ); 01028 } else { 01029 $out->addWikiMsg( 'undeletehistorynoadmin' ); 01030 } 01031 $out->addHTML( '</div>' ); 01032 01033 # List all stored revisions 01034 $revisions = $archive->listRevisions(); 01035 $files = $archive->listFiles(); 01036 01037 $haveRevisions = $revisions && $revisions->numRows() > 0; 01038 $haveFiles = $files && $files->numRows() > 0; 01039 01040 # Batch existence check on user and talk pages 01041 if( $haveRevisions ) { 01042 $batch = new LinkBatch(); 01043 foreach ( $revisions as $row ) { 01044 $batch->addObj( Title::makeTitleSafe( NS_USER, $row->ar_user_text ) ); 01045 $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->ar_user_text ) ); 01046 } 01047 $batch->execute(); 01048 $revisions->seek( 0 ); 01049 } 01050 if( $haveFiles ) { 01051 $batch = new LinkBatch(); 01052 foreach ( $files as $row ) { 01053 $batch->addObj( Title::makeTitleSafe( NS_USER, $row->fa_user_text ) ); 01054 $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->fa_user_text ) ); 01055 } 01056 $batch->execute(); 01057 $files->seek( 0 ); 01058 } 01059 01060 if ( $this->mAllowed ) { 01061 $action = $this->getTitle()->getLocalURL( array( 'action' => 'submit' ) ); 01062 # Start the form here 01063 $top = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'undelete' ) ); 01064 $out->addHTML( $top ); 01065 } 01066 01067 # Show relevant lines from the deletion log: 01068 $out->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) . "\n" ); 01069 LogEventsList::showLogExtract( $out, 'delete', $this->mTargetObj ); 01070 # Show relevant lines from the suppression log: 01071 if( $this->getUser()->isAllowed( 'suppressionlog' ) ) { 01072 $out->addHTML( Xml::element( 'h2', null, LogPage::logName( 'suppress' ) ) . "\n" ); 01073 LogEventsList::showLogExtract( $out, 'suppress', $this->mTargetObj ); 01074 } 01075 01076 if( $this->mAllowed && ( $haveRevisions || $haveFiles ) ) { 01077 # Format the user-visible controls (comment field, submission button) 01078 # in a nice little table 01079 if( $this->getUser()->isAllowed( 'suppressrevision' ) ) { 01080 $unsuppressBox = 01081 "<tr> 01082 <td> </td> 01083 <td class='mw-input'>" . 01084 Xml::checkLabel( $this->msg( 'revdelete-unsuppress' )->text(), 01085 'wpUnsuppress', 'mw-undelete-unsuppress', $this->mUnsuppress ). 01086 "</td> 01087 </tr>"; 01088 } else { 01089 $unsuppressBox = ''; 01090 } 01091 $table = 01092 Xml::fieldset( $this->msg( 'undelete-fieldset-title' )->text() ) . 01093 Xml::openElement( 'table', array( 'id' => 'mw-undelete-table' ) ) . 01094 "<tr> 01095 <td colspan='2' class='mw-undelete-extrahelp'>" . 01096 $this->msg( 'undeleteextrahelp' )->parseAsBlock() . 01097 "</td> 01098 </tr> 01099 <tr> 01100 <td class='mw-label'>" . 01101 Xml::label( $this->msg( 'undeletecomment' )->text(), 'wpComment' ) . 01102 "</td> 01103 <td class='mw-input'>" . 01104 Xml::input( 'wpComment', 50, $this->mComment, array( 'id' => 'wpComment' ) ) . 01105 "</td> 01106 </tr> 01107 <tr> 01108 <td> </td> 01109 <td class='mw-submit'>" . 01110 Xml::submitButton( $this->msg( 'undeletebtn' )->text(), array( 'name' => 'restore', 'id' => 'mw-undelete-submit' ) ) . ' ' . 01111 Xml::submitButton( $this->msg( 'undeleteinvert' )->text(), array( 'name' => 'invert', 'id' => 'mw-undelete-invert' ) ) . 01112 "</td> 01113 </tr>" . 01114 $unsuppressBox . 01115 Xml::closeElement( 'table' ) . 01116 Xml::closeElement( 'fieldset' ); 01117 01118 $out->addHTML( $table ); 01119 } 01120 01121 $out->addHTML( Xml::element( 'h2', null, $this->msg( 'history' )->text() ) . "\n" ); 01122 01123 if( $haveRevisions ) { 01124 # The page's stored (deleted) history: 01125 $out->addHTML( '<ul>' ); 01126 $remaining = $revisions->numRows(); 01127 $earliestLiveTime = $this->mTargetObj->getEarliestRevTime(); 01128 01129 foreach ( $revisions as $row ) { 01130 $remaining--; 01131 $out->addHTML( $this->formatRevisionRow( $row, $earliestLiveTime, $remaining ) ); 01132 } 01133 $revisions->free(); 01134 $out->addHTML( '</ul>' ); 01135 } else { 01136 $out->addWikiMsg( 'nohistory' ); 01137 } 01138 01139 if( $haveFiles ) { 01140 $out->addHTML( Xml::element( 'h2', null, $this->msg( 'filehist' )->text() ) . "\n" ); 01141 $out->addHTML( '<ul>' ); 01142 foreach ( $files as $row ) { 01143 $out->addHTML( $this->formatFileRow( $row ) ); 01144 } 01145 $files->free(); 01146 $out->addHTML( '</ul>' ); 01147 } 01148 01149 if ( $this->mAllowed ) { 01150 # Slip in the hidden controls here 01151 $misc = Html::hidden( 'target', $this->mTarget ); 01152 $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ); 01153 $misc .= Xml::closeElement( 'form' ); 01154 $out->addHTML( $misc ); 01155 } 01156 01157 return true; 01158 } 01159 01160 private function formatRevisionRow( $row, $earliestLiveTime, $remaining ) { 01161 $rev = Revision::newFromArchiveRow( $row, 01162 array( 'page' => $this->mTargetObj->getArticleId() ) ); 01163 $stxt = ''; 01164 $ts = wfTimestamp( TS_MW, $row->ar_timestamp ); 01165 // Build checkboxen... 01166 if( $this->mAllowed ) { 01167 if( $this->mInvert ) { 01168 if( in_array( $ts, $this->mTargetTimestamp ) ) { 01169 $checkBox = Xml::check( "ts$ts" ); 01170 } else { 01171 $checkBox = Xml::check( "ts$ts", true ); 01172 } 01173 } else { 01174 $checkBox = Xml::check( "ts$ts" ); 01175 } 01176 } else { 01177 $checkBox = ''; 01178 } 01179 $user = $this->getUser(); 01180 // Build page & diff links... 01181 if( $this->mCanView ) { 01182 $titleObj = $this->getTitle(); 01183 # Last link 01184 if( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { 01185 $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) ); 01186 $last = $this->msg( 'diff' )->escaped(); 01187 } elseif( $remaining > 0 || ( $earliestLiveTime && $ts > $earliestLiveTime ) ) { 01188 $pageLink = $this->getPageLink( $rev, $titleObj, $ts ); 01189 $last = Linker::linkKnown( 01190 $titleObj, 01191 $this->msg( 'diff' )->escaped(), 01192 array(), 01193 array( 01194 'target' => $this->mTargetObj->getPrefixedText(), 01195 'timestamp' => $ts, 01196 'diff' => 'prev' 01197 ) 01198 ); 01199 } else { 01200 $pageLink = $this->getPageLink( $rev, $titleObj, $ts ); 01201 $last = $this->msg( 'diff' )->escaped(); 01202 } 01203 } else { 01204 $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) ); 01205 $last = $this->msg( 'diff' )->escaped(); 01206 } 01207 // User links 01208 $userLink = Linker::revUserTools( $rev ); 01209 // Revision text size 01210 $size = $row->ar_len; 01211 if( !is_null( $size ) ) { 01212 $stxt = Linker::formatRevisionSize( $size ); 01213 } 01214 // Edit summary 01215 $comment = Linker::revComment( $rev ); 01216 // Revision delete links 01217 $revdlink = Linker::getRevDeleteLink( $user, $rev, $this->mTargetObj ); 01218 return "<li>$checkBox $revdlink ($last) $pageLink . . $userLink $stxt $comment</li>"; 01219 } 01220 01221 private function formatFileRow( $row ) { 01222 $file = ArchivedFile::newFromRow( $row ); 01223 01224 $ts = wfTimestamp( TS_MW, $row->fa_timestamp ); 01225 $user = $this->getUser(); 01226 if( $this->mAllowed && $row->fa_storage_key ) { 01227 $checkBox = Xml::check( 'fileid' . $row->fa_id ); 01228 $key = urlencode( $row->fa_storage_key ); 01229 $pageLink = $this->getFileLink( $file, $this->getTitle(), $ts, $key ); 01230 } else { 01231 $checkBox = ''; 01232 $pageLink = $this->getLanguage()->userTimeAndDate( $ts, $user ); 01233 } 01234 $userLink = $this->getFileUser( $file ); 01235 $data = $this->msg( 'widthheight' )->numParams( $row->fa_width, $row->fa_height )->text() . 01236 ' (' . $this->msg( 'nbytes' )->numParams( $row->fa_size )->text() . ')'; 01237 $data = htmlspecialchars( $data ); 01238 $comment = $this->getFileComment( $file ); 01239 01240 // Add show/hide deletion links if available 01241 $canHide = $user->isAllowed( 'deleterevision' ); 01242 if( $canHide || ( $file->getVisibility() && $user->isAllowed( 'deletedhistory' ) ) ) { 01243 if( !$file->userCan( File::DELETED_RESTRICTED, $user ) ) { 01244 $revdlink = Linker::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops 01245 } else { 01246 $query = array( 01247 'type' => 'filearchive', 01248 'target' => $this->mTargetObj->getPrefixedDBkey(), 01249 'ids' => $row->fa_id 01250 ); 01251 $revdlink = Linker::revDeleteLink( $query, 01252 $file->isDeleted( File::DELETED_RESTRICTED ), $canHide ); 01253 } 01254 } else { 01255 $revdlink = ''; 01256 } 01257 01258 return "<li>$checkBox $revdlink $pageLink . . $userLink $data $comment</li>\n"; 01259 } 01260 01269 function getPageLink( $rev, $titleObj, $ts ) { 01270 $user = $this->getUser(); 01271 $time = $this->getLanguage()->userTimeAndDate( $ts, $user ); 01272 01273 if( !$rev->userCan( Revision::DELETED_TEXT, $user ) ) { 01274 return '<span class="history-deleted">' . $time . '</span>'; 01275 } else { 01276 $link = Linker::linkKnown( 01277 $titleObj, 01278 htmlspecialchars( $time ), 01279 array(), 01280 array( 01281 'target' => $this->mTargetObj->getPrefixedText(), 01282 'timestamp' => $ts 01283 ) 01284 ); 01285 if( $rev->isDeleted( Revision::DELETED_TEXT ) ) { 01286 $link = '<span class="history-deleted">' . $link . '</span>'; 01287 } 01288 return $link; 01289 } 01290 } 01291 01302 function getFileLink( $file, $titleObj, $ts, $key ) { 01303 $user = $this->getUser(); 01304 $time = $this->getLanguage()->userTimeAndDate( $ts, $user ); 01305 01306 if( !$file->userCan( File::DELETED_FILE, $user ) ) { 01307 return '<span class="history-deleted">' . $time . '</span>'; 01308 } else { 01309 $link = Linker::linkKnown( 01310 $titleObj, 01311 htmlspecialchars( $time ), 01312 array(), 01313 array( 01314 'target' => $this->mTargetObj->getPrefixedText(), 01315 'file' => $key, 01316 'token' => $user->getEditToken( $key ) 01317 ) 01318 ); 01319 if( $file->isDeleted( File::DELETED_FILE ) ) { 01320 $link = '<span class="history-deleted">' . $link . '</span>'; 01321 } 01322 return $link; 01323 } 01324 } 01325 01332 function getFileUser( $file ) { 01333 if( !$file->userCan( File::DELETED_USER, $this->getUser() ) ) { 01334 return '<span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>'; 01335 } else { 01336 $link = Linker::userLink( $file->getRawUser(), $file->getRawUserText() ) . 01337 Linker::userToolLinks( $file->getRawUser(), $file->getRawUserText() ); 01338 if( $file->isDeleted( File::DELETED_USER ) ) { 01339 $link = '<span class="history-deleted">' . $link . '</span>'; 01340 } 01341 return $link; 01342 } 01343 } 01344 01351 function getFileComment( $file ) { 01352 if( !$file->userCan( File::DELETED_COMMENT, $this->getUser() ) ) { 01353 return '<span class="history-deleted"><span class="comment">' . 01354 $this->msg( 'rev-deleted-comment' )->escaped() . '</span></span>'; 01355 } else { 01356 $link = Linker::commentBlock( $file->getRawDescription() ); 01357 if( $file->isDeleted( File::DELETED_COMMENT ) ) { 01358 $link = '<span class="history-deleted">' . $link . '</span>'; 01359 } 01360 return $link; 01361 } 01362 } 01363 01364 function undelete() { 01365 global $wgUploadMaintenance; 01366 01367 if ( $wgUploadMaintenance && $this->mTargetObj->getNamespace() == NS_FILE ) { 01368 throw new ErrorPageError( 'undelete-error', 'filedelete-maintenance' ); 01369 } 01370 01371 if ( wfReadOnly() ) { 01372 throw new ReadOnlyError; 01373 } 01374 01375 $out = $this->getOutput(); 01376 $archive = new PageArchive( $this->mTargetObj ); 01377 wfRunHooks( 'UndeleteForm::undelete', array( &$archive, $this->mTargetObj ) ); 01378 $ok = $archive->undelete( 01379 $this->mTargetTimestamp, 01380 $this->mComment, 01381 $this->mFileVersions, 01382 $this->mUnsuppress ); 01383 01384 if( is_array( $ok ) ) { 01385 if ( $ok[1] ) { // Undeleted file count 01386 wfRunHooks( 'FileUndeleteComplete', array( 01387 $this->mTargetObj, $this->mFileVersions, 01388 $this->getUser(), $this->mComment ) ); 01389 } 01390 01391 $link = Linker::linkKnown( $this->mTargetObj ); 01392 $out->addHTML( $this->msg( 'undeletedpage' )->rawParams( $link )->parse() ); 01393 } else { 01394 $out->setPageTitle( $this->msg( 'undelete-error' ) ); 01395 $out->addWikiMsg( 'cannotundelete' ); 01396 $out->addWikiMsg( 'undeleterevdel' ); 01397 } 01398 01399 // Show file deletion warnings and errors 01400 $status = $archive->getFileStatus(); 01401 if( $status && !$status->isGood() ) { 01402 $out->addWikiText( $status->getWikiText( 'undelete-error-short', 'undelete-error-long' ) ); 01403 } 01404 } 01405 }