MediaWiki  REL1_20
SpecialUndelete.php
Go to the documentation of this file.
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>&#160;</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>&#160;</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 }