MediaWiki
REL1_20
|
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' => 'COUNT(*)' 00096 ), 00097 $condition, 00098 __METHOD__, 00099 array( 00100 'GROUP BY' => array( 'ar_namespace', 'ar_title' ), 00101 'ORDER BY' => array( '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 __METHOD__, 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 __METHOD__ 00313 ); 00314 return ( $n > 0 ); 00315 } 00316 00331 function undelete( $timestamps, $comment = '', $fileVersions = array(), $unsuppress = false, User $user = null ) { 00332 global $wgUser; 00333 00334 // If both the set of text revisions and file revisions are empty, 00335 // restore everything. Otherwise, just restore the requested items. 00336 $restoreAll = empty( $timestamps ) && empty( $fileVersions ); 00337 00338 $restoreText = $restoreAll || !empty( $timestamps ); 00339 $restoreFiles = $restoreAll || !empty( $fileVersions ); 00340 00341 if( $restoreFiles && $this->title->getNamespace() == NS_FILE ) { 00342 $img = wfLocalFile( $this->title ); 00343 $this->fileStatus = $img->restore( $fileVersions, $unsuppress ); 00344 if ( !$this->fileStatus->isOk() ) { 00345 return false; 00346 } 00347 $filesRestored = $this->fileStatus->successCount; 00348 } else { 00349 $filesRestored = 0; 00350 } 00351 00352 if( $restoreText ) { 00353 $textRestored = $this->undeleteRevisions( $timestamps, $unsuppress, $comment ); 00354 if( $textRestored === false ) { // It must be one of UNDELETE_* 00355 return false; 00356 } 00357 } else { 00358 $textRestored = 0; 00359 } 00360 00361 // Touch the log! 00362 00363 if( $textRestored && $filesRestored ) { 00364 $reason = wfMessage( 'undeletedrevisions-files' ) 00365 ->numParams( $textRestored, $filesRestored )->inContentLanguage()->text(); 00366 } elseif( $textRestored ) { 00367 $reason = wfMessage( 'undeletedrevisions' )->numParams( $textRestored ) 00368 ->inContentLanguage()->text(); 00369 } elseif( $filesRestored ) { 00370 $reason = wfMessage( 'undeletedfiles' )->numParams( $filesRestored ) 00371 ->inContentLanguage()->text(); 00372 } else { 00373 wfDebug( "Undelete: nothing undeleted...\n" ); 00374 return false; 00375 } 00376 00377 if( trim( $comment ) != '' ) { 00378 $reason .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $comment; 00379 } 00380 00381 if ( $user === null ) { 00382 $user = $wgUser; 00383 } 00384 00385 $logEntry = new ManualLogEntry( 'delete', 'restore' ); 00386 $logEntry->setPerformer( $user ); 00387 $logEntry->setTarget( $this->title ); 00388 $logEntry->setComment( $reason ); 00389 $logid = $logEntry->insert(); 00390 $logEntry->publish( $logid ); 00391 00392 return array( $textRestored, $filesRestored, $reason ); 00393 } 00394 00406 private function undeleteRevisions( $timestamps, $unsuppress = false, $comment = '' ) { 00407 if ( wfReadOnly() ) { 00408 return false; 00409 } 00410 $restoreAll = empty( $timestamps ); 00411 00412 $dbw = wfGetDB( DB_MASTER ); 00413 00414 # Does this page already exist? We'll have to update it... 00415 $article = WikiPage::factory( $this->title ); 00416 # Load latest data for the current page (bug 31179) 00417 $article->loadPageData( 'fromdbmaster' ); 00418 $oldcountable = $article->isCountable(); 00419 00420 $page = $dbw->selectRow( 'page', 00421 array( 'page_id', 'page_latest' ), 00422 array( 'page_namespace' => $this->title->getNamespace(), 00423 'page_title' => $this->title->getDBkey() ), 00424 __METHOD__, 00425 array( 'FOR UPDATE' ) // lock page 00426 ); 00427 if( $page ) { 00428 $makepage = false; 00429 # Page already exists. Import the history, and if necessary 00430 # we'll update the latest revision field in the record. 00431 $newid = 0; 00432 $pageId = $page->page_id; 00433 $previousRevId = $page->page_latest; 00434 # Get the time span of this page 00435 $previousTimestamp = $dbw->selectField( 'revision', 'rev_timestamp', 00436 array( 'rev_id' => $previousRevId ), 00437 __METHOD__ ); 00438 if( $previousTimestamp === false ) { 00439 wfDebug( __METHOD__.": existing page refers to a page_latest that does not exist\n" ); 00440 return 0; 00441 } 00442 } else { 00443 # Have to create a new article... 00444 $makepage = true; 00445 $previousRevId = 0; 00446 $previousTimestamp = 0; 00447 } 00448 00449 if( $restoreAll ) { 00450 $oldones = '1 = 1'; # All revisions... 00451 } else { 00452 $oldts = implode( ',', 00453 array_map( array( &$dbw, 'addQuotes' ), 00454 array_map( array( &$dbw, 'timestamp' ), 00455 $timestamps ) ) ); 00456 00457 $oldones = "ar_timestamp IN ( {$oldts} )"; 00458 } 00459 00463 $result = $dbw->select( 'archive', 00464 /* fields */ array( 00465 'ar_rev_id', 00466 'ar_text', 00467 'ar_comment', 00468 'ar_user', 00469 'ar_user_text', 00470 'ar_timestamp', 00471 'ar_minor_edit', 00472 'ar_flags', 00473 'ar_text_id', 00474 'ar_deleted', 00475 'ar_page_id', 00476 'ar_len', 00477 'ar_sha1' ), 00478 /* WHERE */ array( 00479 'ar_namespace' => $this->title->getNamespace(), 00480 'ar_title' => $this->title->getDBkey(), 00481 $oldones ), 00482 __METHOD__, 00483 /* options */ array( 'ORDER BY' => 'ar_timestamp' ) 00484 ); 00485 $ret = $dbw->resultObject( $result ); 00486 $rev_count = $dbw->numRows( $result ); 00487 if( !$rev_count ) { 00488 wfDebug( __METHOD__ . ": no revisions to restore\n" ); 00489 return false; // ??? 00490 } 00491 00492 $ret->seek( $rev_count - 1 ); // move to last 00493 $row = $ret->fetchObject(); // get newest archived rev 00494 $ret->seek( 0 ); // move back 00495 00496 if( $makepage ) { 00497 // Check the state of the newest to-be version... 00498 if( !$unsuppress && ( $row->ar_deleted & Revision::DELETED_TEXT ) ) { 00499 return false; // we can't leave the current revision like this! 00500 } 00501 // Safe to insert now... 00502 $newid = $article->insertOn( $dbw ); 00503 $pageId = $newid; 00504 } else { 00505 // Check if a deleted revision will become the current revision... 00506 if( $row->ar_timestamp > $previousTimestamp ) { 00507 // Check the state of the newest to-be version... 00508 if( !$unsuppress && ( $row->ar_deleted & Revision::DELETED_TEXT ) ) { 00509 return false; // we can't leave the current revision like this! 00510 } 00511 } 00512 } 00513 00514 $revision = null; 00515 $restored = 0; 00516 00517 foreach ( $ret as $row ) { 00518 // Check for key dupes due to shitty archive integrity. 00519 if( $row->ar_rev_id ) { 00520 $exists = $dbw->selectField( 'revision', '1', 00521 array( 'rev_id' => $row->ar_rev_id ), __METHOD__ ); 00522 if( $exists ) { 00523 continue; // don't throw DB errors 00524 } 00525 } 00526 // Insert one revision at a time...maintaining deletion status 00527 // unless we are specifically removing all restrictions... 00528 $revision = Revision::newFromArchiveRow( $row, 00529 array( 00530 'page' => $pageId, 00531 'deleted' => $unsuppress ? 0 : $row->ar_deleted 00532 ) ); 00533 00534 $revision->insertOn( $dbw ); 00535 $restored++; 00536 00537 wfRunHooks( 'ArticleRevisionUndeleted', array( &$this->title, $revision, $row->ar_page_id ) ); 00538 } 00539 # Now that it's safely stored, take it out of the archive 00540 $dbw->delete( 'archive', 00541 /* WHERE */ array( 00542 'ar_namespace' => $this->title->getNamespace(), 00543 'ar_title' => $this->title->getDBkey(), 00544 $oldones ), 00545 __METHOD__ ); 00546 00547 // Was anything restored at all? 00548 if ( $restored == 0 ) { 00549 return 0; 00550 } 00551 00552 $created = (bool)$newid; 00553 00554 // Attach the latest revision to the page... 00555 $wasnew = $article->updateIfNewerOn( $dbw, $revision, $previousRevId ); 00556 if ( $created || $wasnew ) { 00557 // Update site stats, link tables, etc 00558 $user = User::newFromName( $revision->getRawUserText(), false ); 00559 $article->doEditUpdates( $revision, $user, array( 'created' => $created, 'oldcountable' => $oldcountable ) ); 00560 } 00561 00562 wfRunHooks( 'ArticleUndelete', array( &$this->title, $created, $comment ) ); 00563 00564 if( $this->title->getNamespace() == NS_FILE ) { 00565 $update = new HTMLCacheUpdate( $this->title, 'imagelinks' ); 00566 $update->doUpdate(); 00567 } 00568 00569 return $restored; 00570 } 00571 00575 function getFileStatus() { return $this->fileStatus; } 00576 } 00577 00584 class SpecialUndelete extends SpecialPage { 00585 var $mAction, $mTarget, $mTimestamp, $mRestore, $mInvert, $mFilename; 00586 var $mTargetTimestamp, $mAllowed, $mCanView, $mComment, $mToken; 00587 00591 var $mTargetObj; 00592 00593 function __construct() { 00594 parent::__construct( 'Undelete', 'deletedhistory' ); 00595 } 00596 00597 function loadRequest( $par ) { 00598 $request = $this->getRequest(); 00599 $user = $this->getUser(); 00600 00601 $this->mAction = $request->getVal( 'action' ); 00602 if ( $par !== null && $par !== '' ) { 00603 $this->mTarget = $par; 00604 } else { 00605 $this->mTarget = $request->getVal( 'target' ); 00606 } 00607 $this->mTargetObj = null; 00608 if ( $this->mTarget !== null && $this->mTarget !== '' ) { 00609 $this->mTargetObj = Title::newFromURL( $this->mTarget ); 00610 } 00611 $this->mSearchPrefix = $request->getText( 'prefix' ); 00612 $time = $request->getVal( 'timestamp' ); 00613 $this->mTimestamp = $time ? wfTimestamp( TS_MW, $time ) : ''; 00614 $this->mFilename = $request->getVal( 'file' ); 00615 00616 $posted = $request->wasPosted() && 00617 $user->matchEditToken( $request->getVal( 'wpEditToken' ) ); 00618 $this->mRestore = $request->getCheck( 'restore' ) && $posted; 00619 $this->mInvert = $request->getCheck( 'invert' ) && $posted; 00620 $this->mPreview = $request->getCheck( 'preview' ) && $posted; 00621 $this->mDiff = $request->getCheck( 'diff' ); 00622 $this->mDiffOnly = $request->getBool( 'diffonly', $this->getUser()->getOption( 'diffonly' ) ); 00623 $this->mComment = $request->getText( 'wpComment' ); 00624 $this->mUnsuppress = $request->getVal( 'wpUnsuppress' ) && $user->isAllowed( 'suppressrevision' ); 00625 $this->mToken = $request->getVal( 'token' ); 00626 00627 if ( $user->isAllowed( 'undelete' ) && !$user->isBlocked() ) { 00628 $this->mAllowed = true; // user can restore 00629 $this->mCanView = true; // user can view content 00630 } elseif ( $user->isAllowed( 'deletedtext' ) ) { 00631 $this->mAllowed = false; // user cannot restore 00632 $this->mCanView = true; // user can view content 00633 $this->mRestore = false; 00634 } else { // user can only view the list of revisions 00635 $this->mAllowed = false; 00636 $this->mCanView = false; 00637 $this->mTimestamp = ''; 00638 $this->mRestore = false; 00639 } 00640 00641 if( $this->mRestore || $this->mInvert ) { 00642 $timestamps = array(); 00643 $this->mFileVersions = array(); 00644 foreach( $request->getValues() as $key => $val ) { 00645 $matches = array(); 00646 if( preg_match( '/^ts(\d{14})$/', $key, $matches ) ) { 00647 array_push( $timestamps, $matches[1] ); 00648 } 00649 00650 if( preg_match( '/^fileid(\d+)$/', $key, $matches ) ) { 00651 $this->mFileVersions[] = intval( $matches[1] ); 00652 } 00653 } 00654 rsort( $timestamps ); 00655 $this->mTargetTimestamp = $timestamps; 00656 } 00657 } 00658 00659 function execute( $par ) { 00660 $this->checkPermissions(); 00661 $user = $this->getUser(); 00662 00663 $this->setHeaders(); 00664 $this->outputHeader(); 00665 00666 $this->loadRequest( $par ); 00667 00668 $out = $this->getOutput(); 00669 00670 if ( is_null( $this->mTargetObj ) ) { 00671 $out->addWikiMsg( 'undelete-header' ); 00672 00673 # Not all users can just browse every deleted page from the list 00674 if ( $user->isAllowed( 'browsearchive' ) ) { 00675 $this->showSearchForm(); 00676 } 00677 return; 00678 } 00679 00680 if ( $this->mAllowed ) { 00681 $out->setPageTitle( $this->msg( 'undeletepage' ) ); 00682 } else { 00683 $out->setPageTitle( $this->msg( 'viewdeletedpage' ) ); 00684 } 00685 00686 $this->getSkin()->setRelevantTitle( $this->mTargetObj ); 00687 00688 if ( $this->mTimestamp !== '' ) { 00689 $this->showRevision( $this->mTimestamp ); 00690 } elseif ( $this->mFilename !== null ) { 00691 $file = new ArchivedFile( $this->mTargetObj, '', $this->mFilename ); 00692 // Check if user is allowed to see this file 00693 if ( !$file->exists() ) { 00694 $out->addWikiMsg( 'filedelete-nofile', $this->mFilename ); 00695 } elseif ( !$file->userCan( File::DELETED_FILE, $user ) ) { 00696 if( $file->isDeleted( File::DELETED_RESTRICTED ) ) { 00697 throw new PermissionsError( 'suppressrevision' ); 00698 } else { 00699 throw new PermissionsError( 'deletedtext' ); 00700 } 00701 } elseif ( !$user->matchEditToken( $this->mToken, $this->mFilename ) ) { 00702 $this->showFileConfirmationForm( $this->mFilename ); 00703 } else { 00704 $this->showFile( $this->mFilename ); 00705 } 00706 } elseif ( $this->mRestore && $this->mAction == 'submit' ) { 00707 $this->undelete(); 00708 } else { 00709 $this->showHistory(); 00710 } 00711 } 00712 00713 function showSearchForm() { 00714 global $wgScript; 00715 00716 $out = $this->getOutput(); 00717 $out->setPageTitle( $this->msg( 'undelete-search-title' ) ); 00718 $out->addHTML( 00719 Xml::openElement( 'form', array( 00720 'method' => 'get', 00721 'action' => $wgScript ) ) . 00722 Xml::fieldset( $this->msg( 'undelete-search-box' )->text() ) . 00723 Html::hidden( 'title', 00724 $this->getTitle()->getPrefixedDbKey() ) . 00725 Xml::inputLabel( $this->msg( 'undelete-search-prefix' )->text(), 00726 'prefix', 'prefix', 20, 00727 $this->mSearchPrefix ) . ' ' . 00728 Xml::submitButton( $this->msg( 'undelete-search-submit' )->text() ) . 00729 Xml::closeElement( 'fieldset' ) . 00730 Xml::closeElement( 'form' ) 00731 ); 00732 00733 # List undeletable articles 00734 if( $this->mSearchPrefix ) { 00735 $result = PageArchive::listPagesByPrefix( $this->mSearchPrefix ); 00736 $this->showList( $result ); 00737 } 00738 } 00739 00746 private function showList( $result ) { 00747 $out = $this->getOutput(); 00748 00749 if( $result->numRows() == 0 ) { 00750 $out->addWikiMsg( 'undelete-no-results' ); 00751 return; 00752 } 00753 00754 $out->addWikiMsg( 'undeletepagetext', $this->getLanguage()->formatNum( $result->numRows() ) ); 00755 00756 $undelete = $this->getTitle(); 00757 $out->addHTML( "<ul>\n" ); 00758 foreach ( $result as $row ) { 00759 $title = Title::makeTitleSafe( $row->ar_namespace, $row->ar_title ); 00760 if ( $title !== null ) { 00761 $item = Linker::linkKnown( 00762 $undelete, 00763 htmlspecialchars( $title->getPrefixedText() ), 00764 array(), 00765 array( 'target' => $title->getPrefixedText() ) 00766 ); 00767 } else { 00768 // The title is no longer valid, show as text 00769 $item = Html::element( 'span', array( 'class' => 'mw-invalidtitle' ), 00770 Linker::getInvalidTitleDescription( $this->getContext(), $row->ar_namespace, $row->ar_title ) ); 00771 } 00772 $revs = $this->msg( 'undeleterevisions' )->numParams( $row->count )->parse(); 00773 $out->addHTML( "<li>{$item} ({$revs})</li>\n" ); 00774 } 00775 $result->free(); 00776 $out->addHTML( "</ul>\n" ); 00777 00778 return true; 00779 } 00780 00781 private function showRevision( $timestamp ) { 00782 if( !preg_match( '/[0-9]{14}/', $timestamp ) ) { 00783 return 0; 00784 } 00785 00786 $archive = new PageArchive( $this->mTargetObj ); 00787 wfRunHooks( 'UndeleteForm::showRevision', array( &$archive, $this->mTargetObj ) ); 00788 $rev = $archive->getRevision( $timestamp ); 00789 00790 $out = $this->getOutput(); 00791 $user = $this->getUser(); 00792 00793 if( !$rev ) { 00794 $out->addWikiMsg( 'undeleterevision-missing' ); 00795 return; 00796 } 00797 00798 if( $rev->isDeleted( Revision::DELETED_TEXT ) ) { 00799 if( !$rev->userCan( Revision::DELETED_TEXT, $user ) ) { 00800 $out->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", 'rev-deleted-text-permission' ); 00801 return; 00802 } else { 00803 $out->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", 'rev-deleted-text-view' ); 00804 $out->addHTML( '<br />' ); 00805 // and we are allowed to see... 00806 } 00807 } 00808 00809 if( $this->mDiff ) { 00810 $previousRev = $archive->getPreviousRevision( $timestamp ); 00811 if( $previousRev ) { 00812 $this->showDiff( $previousRev, $rev ); 00813 if( $this->mDiffOnly ) { 00814 return; 00815 } else { 00816 $out->addHTML( '<hr />' ); 00817 } 00818 } else { 00819 $out->addWikiMsg( 'undelete-nodiff' ); 00820 } 00821 } 00822 00823 $link = Linker::linkKnown( 00824 $this->getTitle( $this->mTargetObj->getPrefixedDBkey() ), 00825 htmlspecialchars( $this->mTargetObj->getPrefixedText() ) 00826 ); 00827 00828 $lang = $this->getLanguage(); 00829 00830 // date and time are separate parameters to facilitate localisation. 00831 // $time is kept for backward compat reasons. 00832 $time = $lang->userTimeAndDate( $timestamp, $user ); 00833 $d = $lang->userDate( $timestamp, $user ); 00834 $t = $lang->userTime( $timestamp, $user ); 00835 $userLink = Linker::revUserTools( $rev ); 00836 00837 if( $this->mPreview ) { 00838 $openDiv = '<div id="mw-undelete-revision" class="mw-warning">'; 00839 } else { 00840 $openDiv = '<div id="mw-undelete-revision">'; 00841 } 00842 $out->addHTML( $openDiv ); 00843 00844 // Revision delete links 00845 if ( !$this->mDiff ) { 00846 $revdel = Linker::getRevDeleteLink( $user, $rev, $this->mTargetObj ); 00847 if ( $revdel ) { 00848 $out->addHTML( "$revdel " ); 00849 } 00850 } 00851 00852 $out->addHTML( $this->msg( 'undelete-revision' )->rawParams( $link )->params( 00853 $time )->rawParams( $userLink )->params( $d, $t )->parse() . '</div>' ); 00854 wfRunHooks( 'UndeleteShowRevision', array( $this->mTargetObj, $rev ) ); 00855 00856 if( $this->mPreview ) { 00857 // Hide [edit]s 00858 $popts = $out->parserOptions(); 00859 $popts->setEditSection( false ); 00860 $out->parserOptions( $popts ); 00861 $out->addWikiTextTitleTidy( $rev->getText( Revision::FOR_THIS_USER, $user ), $this->mTargetObj, true ); 00862 } 00863 00864 $out->addHTML( 00865 Xml::element( 'textarea', array( 00866 'readonly' => 'readonly', 00867 'cols' => intval( $user->getOption( 'cols' ) ), 00868 'rows' => intval( $user->getOption( 'rows' ) ) ), 00869 $rev->getText( Revision::FOR_THIS_USER, $user ) . "\n" ) . 00870 Xml::openElement( 'div' ) . 00871 Xml::openElement( 'form', array( 00872 'method' => 'post', 00873 'action' => $this->getTitle()->getLocalURL( array( 'action' => 'submit' ) ) ) ) . 00874 Xml::element( 'input', array( 00875 'type' => 'hidden', 00876 'name' => 'target', 00877 'value' => $this->mTargetObj->getPrefixedDbKey() ) ) . 00878 Xml::element( 'input', array( 00879 'type' => 'hidden', 00880 'name' => 'timestamp', 00881 'value' => $timestamp ) ) . 00882 Xml::element( 'input', array( 00883 'type' => 'hidden', 00884 'name' => 'wpEditToken', 00885 'value' => $user->getEditToken() ) ) . 00886 Xml::element( 'input', array( 00887 'type' => 'submit', 00888 'name' => 'preview', 00889 'value' => $this->msg( 'showpreview' )->text() ) ) . 00890 Xml::element( 'input', array( 00891 'name' => 'diff', 00892 'type' => 'submit', 00893 'value' => $this->msg( 'showdiff' )->text() ) ) . 00894 Xml::closeElement( 'form' ) . 00895 Xml::closeElement( 'div' ) ); 00896 } 00897 00906 function showDiff( $previousRev, $currentRev ) { 00907 $diffEngine = new DifferenceEngine( $this->getContext() ); 00908 $diffEngine->showDiffStyle(); 00909 $this->getOutput()->addHTML( 00910 "<div>" . 00911 "<table width='98%' cellpadding='0' cellspacing='4' class='diff'>" . 00912 "<col class='diff-marker' />" . 00913 "<col class='diff-content' />" . 00914 "<col class='diff-marker' />" . 00915 "<col class='diff-content' />" . 00916 "<tr>" . 00917 "<td colspan='2' width='50%' style='text-align: center' class='diff-otitle'>" . 00918 $this->diffHeader( $previousRev, 'o' ) . 00919 "</td>\n" . 00920 "<td colspan='2' width='50%' style='text-align: center' class='diff-ntitle'>" . 00921 $this->diffHeader( $currentRev, 'n' ) . 00922 "</td>\n" . 00923 "</tr>" . 00924 $diffEngine->generateDiffBody( 00925 $previousRev->getText( Revision::FOR_THIS_USER, $this->getUser() ), 00926 $currentRev->getText( Revision::FOR_THIS_USER, $this->getUser() ) ) . 00927 "</table>" . 00928 "</div>\n" 00929 ); 00930 } 00931 00937 private function diffHeader( $rev, $prefix ) { 00938 $isDeleted = !( $rev->getId() && $rev->getTitle() ); 00939 if( $isDeleted ) { 00941 $targetPage = $this->getTitle(); 00942 $targetQuery = array( 00943 'target' => $this->mTargetObj->getPrefixedText(), 00944 'timestamp' => wfTimestamp( TS_MW, $rev->getTimestamp() ) 00945 ); 00946 } else { 00948 $targetPage = $rev->getTitle(); 00949 $targetQuery = array( 'oldid' => $rev->getId() ); 00950 } 00951 // Add show/hide deletion links if available 00952 $user = $this->getUser(); 00953 $lang = $this->getLanguage(); 00954 $rdel = Linker::getRevDeleteLink( $user, $rev, $this->mTargetObj ); 00955 if ( $rdel ) $rdel = " $rdel"; 00956 return 00957 '<div id="mw-diff-' . $prefix . 'title1"><strong>' . 00958 Linker::link( 00959 $targetPage, 00960 $this->msg( 00961 'revisionasof', 00962 $lang->userTimeAndDate( $rev->getTimestamp(), $user ), 00963 $lang->userDate( $rev->getTimestamp(), $user ), 00964 $lang->userTime( $rev->getTimestamp(), $user ) 00965 )->escaped(), 00966 array(), 00967 $targetQuery 00968 ) . 00969 '</strong></div>' . 00970 '<div id="mw-diff-'.$prefix.'title2">' . 00971 Linker::revUserTools( $rev ) . '<br />' . 00972 '</div>' . 00973 '<div id="mw-diff-'.$prefix.'title3">' . 00974 Linker::revComment( $rev ) . $rdel . '<br />' . 00975 '</div>'; 00976 } 00977 00981 private function showFileConfirmationForm( $key ) { 00982 $out = $this->getOutput(); 00983 $lang = $this->getLanguage(); 00984 $user = $this->getUser(); 00985 $file = new ArchivedFile( $this->mTargetObj, '', $this->mFilename ); 00986 $out->addWikiMsg( 'undelete-show-file-confirm', 00987 $this->mTargetObj->getText(), 00988 $lang->userDate( $file->getTimestamp(), $user ), 00989 $lang->userTime( $file->getTimestamp(), $user ) ); 00990 $out->addHTML( 00991 Xml::openElement( 'form', array( 00992 'method' => 'POST', 00993 'action' => $this->getTitle()->getLocalURL( 00994 'target=' . urlencode( $this->mTarget ) . 00995 '&file=' . urlencode( $key ) . 00996 '&token=' . urlencode( $user->getEditToken( $key ) ) ) 00997 ) 00998 ) . 00999 Xml::submitButton( $this->msg( 'undelete-show-file-submit' )->text() ) . 01000 '</form>' 01001 ); 01002 } 01003 01007 private function showFile( $key ) { 01008 $this->getOutput()->disable(); 01009 01010 # We mustn't allow the output to be Squid cached, otherwise 01011 # if an admin previews a deleted image, and it's cached, then 01012 # a user without appropriate permissions can toddle off and 01013 # nab the image, and Squid will serve it 01014 $response = $this->getRequest()->response(); 01015 $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' ); 01016 $response->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' ); 01017 $response->header( 'Pragma: no-cache' ); 01018 01019 $repo = RepoGroup::singleton()->getLocalRepo(); 01020 $path = $repo->getZonePath( 'deleted' ) . '/' . $repo->getDeletedHashPath( $key ) . $key; 01021 $repo->streamFile( $path ); 01022 } 01023 01024 private function showHistory() { 01025 $out = $this->getOutput(); 01026 if( $this->mAllowed ) { 01027 $out->addModules( 'mediawiki.special.undelete' ); 01028 } 01029 $out->wrapWikiMsg( 01030 "<div class='mw-undelete-pagetitle'>\n$1\n</div>\n", 01031 array( 'undeletepagetitle', wfEscapeWikiText( $this->mTargetObj->getPrefixedText() ) ) 01032 ); 01033 01034 $archive = new PageArchive( $this->mTargetObj ); 01035 wfRunHooks( 'UndeleteForm::showHistory', array( &$archive, $this->mTargetObj ) ); 01036 /* 01037 $text = $archive->getLastRevisionText(); 01038 if( is_null( $text ) ) { 01039 $out->addWikiMsg( 'nohistory' ); 01040 return; 01041 } 01042 */ 01043 $out->addHTML( '<div class="mw-undelete-history">' ); 01044 if ( $this->mAllowed ) { 01045 $out->addWikiMsg( 'undeletehistory' ); 01046 $out->addWikiMsg( 'undeleterevdel' ); 01047 } else { 01048 $out->addWikiMsg( 'undeletehistorynoadmin' ); 01049 } 01050 $out->addHTML( '</div>' ); 01051 01052 # List all stored revisions 01053 $revisions = $archive->listRevisions(); 01054 $files = $archive->listFiles(); 01055 01056 $haveRevisions = $revisions && $revisions->numRows() > 0; 01057 $haveFiles = $files && $files->numRows() > 0; 01058 01059 # Batch existence check on user and talk pages 01060 if( $haveRevisions ) { 01061 $batch = new LinkBatch(); 01062 foreach ( $revisions as $row ) { 01063 $batch->addObj( Title::makeTitleSafe( NS_USER, $row->ar_user_text ) ); 01064 $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->ar_user_text ) ); 01065 } 01066 $batch->execute(); 01067 $revisions->seek( 0 ); 01068 } 01069 if( $haveFiles ) { 01070 $batch = new LinkBatch(); 01071 foreach ( $files as $row ) { 01072 $batch->addObj( Title::makeTitleSafe( NS_USER, $row->fa_user_text ) ); 01073 $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->fa_user_text ) ); 01074 } 01075 $batch->execute(); 01076 $files->seek( 0 ); 01077 } 01078 01079 if ( $this->mAllowed ) { 01080 $action = $this->getTitle()->getLocalURL( array( 'action' => 'submit' ) ); 01081 # Start the form here 01082 $top = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'undelete' ) ); 01083 $out->addHTML( $top ); 01084 } 01085 01086 # Show relevant lines from the deletion log: 01087 $deleteLogPage = new LogPage( 'delete' ); 01088 $out->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) . "\n" ); 01089 LogEventsList::showLogExtract( $out, 'delete', $this->mTargetObj ); 01090 # Show relevant lines from the suppression log: 01091 $suppressLogPage = new LogPage( 'suppress' ); 01092 if( $this->getUser()->isAllowed( 'suppressionlog' ) ) { 01093 $out->addHTML( Xml::element( 'h2', null, $suppressLogPage->getName()->text() ) . "\n" ); 01094 LogEventsList::showLogExtract( $out, 'suppress', $this->mTargetObj ); 01095 } 01096 01097 if( $this->mAllowed && ( $haveRevisions || $haveFiles ) ) { 01098 # Format the user-visible controls (comment field, submission button) 01099 # in a nice little table 01100 if( $this->getUser()->isAllowed( 'suppressrevision' ) ) { 01101 $unsuppressBox = 01102 "<tr> 01103 <td> </td> 01104 <td class='mw-input'>" . 01105 Xml::checkLabel( $this->msg( 'revdelete-unsuppress' )->text(), 01106 'wpUnsuppress', 'mw-undelete-unsuppress', $this->mUnsuppress ). 01107 "</td> 01108 </tr>"; 01109 } else { 01110 $unsuppressBox = ''; 01111 } 01112 $table = 01113 Xml::fieldset( $this->msg( 'undelete-fieldset-title' )->text() ) . 01114 Xml::openElement( 'table', array( 'id' => 'mw-undelete-table' ) ) . 01115 "<tr> 01116 <td colspan='2' class='mw-undelete-extrahelp'>" . 01117 $this->msg( 'undeleteextrahelp' )->parseAsBlock() . 01118 "</td> 01119 </tr> 01120 <tr> 01121 <td class='mw-label'>" . 01122 Xml::label( $this->msg( 'undeletecomment' )->text(), 'wpComment' ) . 01123 "</td> 01124 <td class='mw-input'>" . 01125 Xml::input( 'wpComment', 50, $this->mComment, array( 'id' => 'wpComment' ) ) . 01126 "</td> 01127 </tr> 01128 <tr> 01129 <td> </td> 01130 <td class='mw-submit'>" . 01131 Xml::submitButton( $this->msg( 'undeletebtn' )->text(), array( 'name' => 'restore', 'id' => 'mw-undelete-submit' ) ) . ' ' . 01132 Xml::submitButton( $this->msg( 'undeleteinvert' )->text(), array( 'name' => 'invert', 'id' => 'mw-undelete-invert' ) ) . 01133 "</td> 01134 </tr>" . 01135 $unsuppressBox . 01136 Xml::closeElement( 'table' ) . 01137 Xml::closeElement( 'fieldset' ); 01138 01139 $out->addHTML( $table ); 01140 } 01141 01142 $out->addHTML( Xml::element( 'h2', null, $this->msg( 'history' )->text() ) . "\n" ); 01143 01144 if( $haveRevisions ) { 01145 # The page's stored (deleted) history: 01146 $out->addHTML( '<ul>' ); 01147 $remaining = $revisions->numRows(); 01148 $earliestLiveTime = $this->mTargetObj->getEarliestRevTime(); 01149 01150 foreach ( $revisions as $row ) { 01151 $remaining--; 01152 $out->addHTML( $this->formatRevisionRow( $row, $earliestLiveTime, $remaining ) ); 01153 } 01154 $revisions->free(); 01155 $out->addHTML( '</ul>' ); 01156 } else { 01157 $out->addWikiMsg( 'nohistory' ); 01158 } 01159 01160 if( $haveFiles ) { 01161 $out->addHTML( Xml::element( 'h2', null, $this->msg( 'filehist' )->text() ) . "\n" ); 01162 $out->addHTML( '<ul>' ); 01163 foreach ( $files as $row ) { 01164 $out->addHTML( $this->formatFileRow( $row ) ); 01165 } 01166 $files->free(); 01167 $out->addHTML( '</ul>' ); 01168 } 01169 01170 if ( $this->mAllowed ) { 01171 # Slip in the hidden controls here 01172 $misc = Html::hidden( 'target', $this->mTarget ); 01173 $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ); 01174 $misc .= Xml::closeElement( 'form' ); 01175 $out->addHTML( $misc ); 01176 } 01177 01178 return true; 01179 } 01180 01181 private function formatRevisionRow( $row, $earliestLiveTime, $remaining ) { 01182 $rev = Revision::newFromArchiveRow( $row, 01183 array( 'page' => $this->mTargetObj->getArticleID() ) ); 01184 $revTextSize = ''; 01185 $ts = wfTimestamp( TS_MW, $row->ar_timestamp ); 01186 // Build checkboxen... 01187 if( $this->mAllowed ) { 01188 if( $this->mInvert ) { 01189 if( in_array( $ts, $this->mTargetTimestamp ) ) { 01190 $checkBox = Xml::check( "ts$ts" ); 01191 } else { 01192 $checkBox = Xml::check( "ts$ts", true ); 01193 } 01194 } else { 01195 $checkBox = Xml::check( "ts$ts" ); 01196 } 01197 } else { 01198 $checkBox = ''; 01199 } 01200 $user = $this->getUser(); 01201 // Build page & diff links... 01202 if( $this->mCanView ) { 01203 $titleObj = $this->getTitle(); 01204 # Last link 01205 if( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { 01206 $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) ); 01207 $last = $this->msg( 'diff' )->escaped(); 01208 } elseif( $remaining > 0 || ( $earliestLiveTime && $ts > $earliestLiveTime ) ) { 01209 $pageLink = $this->getPageLink( $rev, $titleObj, $ts ); 01210 $last = Linker::linkKnown( 01211 $titleObj, 01212 $this->msg( 'diff' )->escaped(), 01213 array(), 01214 array( 01215 'target' => $this->mTargetObj->getPrefixedText(), 01216 'timestamp' => $ts, 01217 'diff' => 'prev' 01218 ) 01219 ); 01220 } else { 01221 $pageLink = $this->getPageLink( $rev, $titleObj, $ts ); 01222 $last = $this->msg( 'diff' )->escaped(); 01223 } 01224 } else { 01225 $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) ); 01226 $last = $this->msg( 'diff' )->escaped(); 01227 } 01228 // User links 01229 $userLink = Linker::revUserTools( $rev ); 01230 // Revision text size 01231 $size = $row->ar_len; 01232 if( !is_null( $size ) ) { 01233 $revTextSize = Linker::formatRevisionSize( $size ); 01234 } 01235 // Edit summary 01236 $comment = Linker::revComment( $rev ); 01237 // Revision delete links 01238 $revdlink = Linker::getRevDeleteLink( $user, $rev, $this->mTargetObj ); 01239 01240 $revisionRow = $this->msg( 'undelete-revisionrow' )->rawParams( $checkBox, $revdlink, $last, $pageLink , $userLink, $revTextSize, $comment )->escaped(); 01241 return "<li>$revisionRow</li>"; 01242 } 01243 01244 private function formatFileRow( $row ) { 01245 $file = ArchivedFile::newFromRow( $row ); 01246 01247 $ts = wfTimestamp( TS_MW, $row->fa_timestamp ); 01248 $user = $this->getUser(); 01249 if( $this->mAllowed && $row->fa_storage_key ) { 01250 $checkBox = Xml::check( 'fileid' . $row->fa_id ); 01251 $key = urlencode( $row->fa_storage_key ); 01252 $pageLink = $this->getFileLink( $file, $this->getTitle(), $ts, $key ); 01253 } else { 01254 $checkBox = ''; 01255 $pageLink = $this->getLanguage()->userTimeAndDate( $ts, $user ); 01256 } 01257 $userLink = $this->getFileUser( $file ); 01258 $data = $this->msg( 'widthheight' )->numParams( $row->fa_width, $row->fa_height )->text(); 01259 $bytes = $this->msg( 'parentheses' )->rawParams( $this->msg( 'nbytes' )->numParams( $row->fa_size )->text() )->plain(); 01260 $data = htmlspecialchars( $data . ' ' . $bytes ); 01261 $comment = $this->getFileComment( $file ); 01262 01263 // Add show/hide deletion links if available 01264 $canHide = $user->isAllowed( 'deleterevision' ); 01265 if( $canHide || ( $file->getVisibility() && $user->isAllowed( 'deletedhistory' ) ) ) { 01266 if( !$file->userCan( File::DELETED_RESTRICTED, $user ) ) { 01267 $revdlink = Linker::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops 01268 } else { 01269 $query = array( 01270 'type' => 'filearchive', 01271 'target' => $this->mTargetObj->getPrefixedDBkey(), 01272 'ids' => $row->fa_id 01273 ); 01274 $revdlink = Linker::revDeleteLink( $query, 01275 $file->isDeleted( File::DELETED_RESTRICTED ), $canHide ); 01276 } 01277 } else { 01278 $revdlink = ''; 01279 } 01280 01281 return "<li>$checkBox $revdlink $pageLink . . $userLink $data $comment</li>\n"; 01282 } 01283 01292 function getPageLink( $rev, $titleObj, $ts ) { 01293 $user = $this->getUser(); 01294 $time = $this->getLanguage()->userTimeAndDate( $ts, $user ); 01295 01296 if( !$rev->userCan( Revision::DELETED_TEXT, $user ) ) { 01297 return '<span class="history-deleted">' . $time . '</span>'; 01298 } else { 01299 $link = Linker::linkKnown( 01300 $titleObj, 01301 htmlspecialchars( $time ), 01302 array(), 01303 array( 01304 'target' => $this->mTargetObj->getPrefixedText(), 01305 'timestamp' => $ts 01306 ) 01307 ); 01308 if( $rev->isDeleted( Revision::DELETED_TEXT ) ) { 01309 $link = '<span class="history-deleted">' . $link . '</span>'; 01310 } 01311 return $link; 01312 } 01313 } 01314 01325 function getFileLink( $file, $titleObj, $ts, $key ) { 01326 $user = $this->getUser(); 01327 $time = $this->getLanguage()->userTimeAndDate( $ts, $user ); 01328 01329 if( !$file->userCan( File::DELETED_FILE, $user ) ) { 01330 return '<span class="history-deleted">' . $time . '</span>'; 01331 } else { 01332 $link = Linker::linkKnown( 01333 $titleObj, 01334 htmlspecialchars( $time ), 01335 array(), 01336 array( 01337 'target' => $this->mTargetObj->getPrefixedText(), 01338 'file' => $key, 01339 'token' => $user->getEditToken( $key ) 01340 ) 01341 ); 01342 if( $file->isDeleted( File::DELETED_FILE ) ) { 01343 $link = '<span class="history-deleted">' . $link . '</span>'; 01344 } 01345 return $link; 01346 } 01347 } 01348 01355 function getFileUser( $file ) { 01356 if( !$file->userCan( File::DELETED_USER, $this->getUser() ) ) { 01357 return '<span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>'; 01358 } else { 01359 $link = Linker::userLink( $file->getRawUser(), $file->getRawUserText() ) . 01360 Linker::userToolLinks( $file->getRawUser(), $file->getRawUserText() ); 01361 if( $file->isDeleted( File::DELETED_USER ) ) { 01362 $link = '<span class="history-deleted">' . $link . '</span>'; 01363 } 01364 return $link; 01365 } 01366 } 01367 01374 function getFileComment( $file ) { 01375 if( !$file->userCan( File::DELETED_COMMENT, $this->getUser() ) ) { 01376 return '<span class="history-deleted"><span class="comment">' . 01377 $this->msg( 'rev-deleted-comment' )->escaped() . '</span></span>'; 01378 } else { 01379 $link = Linker::commentBlock( $file->getRawDescription() ); 01380 if( $file->isDeleted( File::DELETED_COMMENT ) ) { 01381 $link = '<span class="history-deleted">' . $link . '</span>'; 01382 } 01383 return $link; 01384 } 01385 } 01386 01387 function undelete() { 01388 global $wgUploadMaintenance; 01389 01390 if ( $wgUploadMaintenance && $this->mTargetObj->getNamespace() == NS_FILE ) { 01391 throw new ErrorPageError( 'undelete-error', 'filedelete-maintenance' ); 01392 } 01393 01394 if ( wfReadOnly() ) { 01395 throw new ReadOnlyError; 01396 } 01397 01398 $out = $this->getOutput(); 01399 $archive = new PageArchive( $this->mTargetObj ); 01400 wfRunHooks( 'UndeleteForm::undelete', array( &$archive, $this->mTargetObj ) ); 01401 $ok = $archive->undelete( 01402 $this->mTargetTimestamp, 01403 $this->mComment, 01404 $this->mFileVersions, 01405 $this->mUnsuppress, 01406 $this->getUser() 01407 ); 01408 01409 if( is_array( $ok ) ) { 01410 if ( $ok[1] ) { // Undeleted file count 01411 wfRunHooks( 'FileUndeleteComplete', array( 01412 $this->mTargetObj, $this->mFileVersions, 01413 $this->getUser(), $this->mComment ) ); 01414 } 01415 01416 $link = Linker::linkKnown( $this->mTargetObj ); 01417 $out->addHTML( $this->msg( 'undeletedpage' )->rawParams( $link )->parse() ); 01418 } else { 01419 $out->setPageTitle( $this->msg( 'undelete-error' ) ); 01420 $out->addWikiMsg( 'cannotundelete' ); 01421 $out->addWikiMsg( 'undeleterevdel' ); 01422 } 01423 01424 // Show file deletion warnings and errors 01425 $status = $archive->getFileStatus(); 01426 if( $status && !$status->isGood() ) { 01427 $out->addWikiText( '<div class="error">' . $status->getWikiText( 'undelete-error-short', 'undelete-error-long' ) . '</div>' ); 01428 } 01429 } 01430 }