MediaWiki  REL1_22
RevisionDelete.php
Go to the documentation of this file.
00001 <?php
00033 class RevDel_RevisionList extends RevDel_List {
00034     var $currentRevId;
00035 
00036     public function getType() {
00037         return 'revision';
00038     }
00039 
00040     public static function getRelationType() {
00041         return 'rev_id';
00042     }
00043 
00044     public static function getRestriction() {
00045         return 'deleterevision';
00046     }
00047 
00048     public static function getRevdelConstant() {
00049         return Revision::DELETED_TEXT;
00050     }
00051 
00052     public static function suggestTarget( $target, array $ids ) {
00053         $rev = Revision::newFromId( $ids[0] );
00054         return $rev ? $rev->getTitle() : $target;
00055     }
00056 
00061     public function doQuery( $db ) {
00062         $ids = array_map( 'intval', $this->ids );
00063         $live = $db->select(
00064             array( 'revision', 'page', 'user' ),
00065             array_merge( Revision::selectFields(), Revision::selectUserFields() ),
00066             array(
00067                 'rev_page' => $this->title->getArticleID(),
00068                 'rev_id' => $ids,
00069             ),
00070             __METHOD__,
00071             array( 'ORDER BY' => 'rev_id DESC' ),
00072             array(
00073                 'page' => Revision::pageJoinCond(),
00074                 'user' => Revision::userJoinCond() )
00075         );
00076 
00077         if ( $live->numRows() >= count( $ids ) ) {
00078             // All requested revisions are live, keeps things simple!
00079             return $live;
00080         }
00081 
00082         // Check if any requested revisions are available fully deleted.
00083         $archived = $db->select( array( 'archive' ), '*',
00084             array(
00085                 'ar_rev_id' => $ids
00086             ),
00087             __METHOD__,
00088             array( 'ORDER BY' => 'ar_rev_id DESC' )
00089         );
00090 
00091         if ( $archived->numRows() == 0 ) {
00092             return $live;
00093         } elseif ( $live->numRows() == 0 ) {
00094             return $archived;
00095         } else {
00096             // Combine the two! Whee
00097             $rows = array();
00098             foreach ( $live as $row ) {
00099                 $rows[$row->rev_id] = $row;
00100             }
00101             foreach ( $archived as $row ) {
00102                 $rows[$row->ar_rev_id] = $row;
00103             }
00104             krsort( $rows );
00105             return new FakeResultWrapper( array_values( $rows ) );
00106         }
00107     }
00108 
00109     public function newItem( $row ) {
00110         if ( isset( $row->rev_id ) ) {
00111             return new RevDel_RevisionItem( $this, $row );
00112         } elseif ( isset( $row->ar_rev_id ) ) {
00113             return new RevDel_ArchivedRevisionItem( $this, $row );
00114         } else {
00115             // This shouldn't happen. :)
00116             throw new MWException( 'Invalid row type in RevDel_RevisionList' );
00117         }
00118     }
00119 
00120     public function getCurrent() {
00121         if ( is_null( $this->currentRevId ) ) {
00122             $dbw = wfGetDB( DB_MASTER );
00123             $this->currentRevId = $dbw->selectField(
00124                 'page', 'page_latest', $this->title->pageCond(), __METHOD__ );
00125         }
00126         return $this->currentRevId;
00127     }
00128 
00129     public function getSuppressBit() {
00130         return Revision::DELETED_RESTRICTED;
00131     }
00132 
00133     public function doPreCommitUpdates() {
00134         $this->title->invalidateCache();
00135         return Status::newGood();
00136     }
00137 
00138     public function doPostCommitUpdates() {
00139         $this->title->purgeSquid();
00140         // Extensions that require referencing previous revisions may need this
00141         wfRunHooks( 'ArticleRevisionVisibilitySet', array( &$this->title ) );
00142         return Status::newGood();
00143     }
00144 }
00145 
00149 class RevDel_RevisionItem extends RevDel_Item {
00150     var $revision;
00151 
00152     public function __construct( $list, $row ) {
00153         parent::__construct( $list, $row );
00154         $this->revision = new Revision( $row );
00155     }
00156 
00157     public function getIdField() {
00158         return 'rev_id';
00159     }
00160 
00161     public function getTimestampField() {
00162         return 'rev_timestamp';
00163     }
00164 
00165     public function getAuthorIdField() {
00166         return 'rev_user';
00167     }
00168 
00169     public function getAuthorNameField() {
00170         return 'user_name'; // see Revision::selectUserFields()
00171     }
00172 
00173     public function canView() {
00174         return $this->revision->userCan( Revision::DELETED_RESTRICTED, $this->list->getUser() );
00175     }
00176 
00177     public function canViewContent() {
00178         return $this->revision->userCan( Revision::DELETED_TEXT, $this->list->getUser() );
00179     }
00180 
00181     public function getBits() {
00182         return $this->revision->getVisibility();
00183     }
00184 
00185     public function setBits( $bits ) {
00186         $dbw = wfGetDB( DB_MASTER );
00187         // Update revision table
00188         $dbw->update( 'revision',
00189             array( 'rev_deleted' => $bits ),
00190             array(
00191                 'rev_id' => $this->revision->getId(),
00192                 'rev_page' => $this->revision->getPage(),
00193                 'rev_deleted' => $this->getBits()
00194             ),
00195             __METHOD__
00196         );
00197         if ( !$dbw->affectedRows() ) {
00198             // Concurrent fail!
00199             return false;
00200         }
00201         // Update recentchanges table
00202         $dbw->update( 'recentchanges',
00203             array(
00204                 'rc_deleted' => $bits,
00205                 'rc_patrolled' => 1
00206             ),
00207             array(
00208                 'rc_this_oldid' => $this->revision->getId(), // condition
00209                 // non-unique timestamp index
00210                 'rc_timestamp' => $dbw->timestamp( $this->revision->getTimestamp() ),
00211             ),
00212             __METHOD__
00213         );
00214         return true;
00215     }
00216 
00217     public function isDeleted() {
00218         return $this->revision->isDeleted( Revision::DELETED_TEXT );
00219     }
00220 
00221     public function isHideCurrentOp( $newBits ) {
00222         return ( $newBits & Revision::DELETED_TEXT )
00223             && $this->list->getCurrent() == $this->getId();
00224     }
00225 
00231     protected function getRevisionLink() {
00232         $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate(
00233             $this->revision->getTimestamp(), $this->list->getUser() ) );
00234 
00235         if ( $this->isDeleted() && !$this->canViewContent() ) {
00236             return $date;
00237         }
00238         return Linker::linkKnown(
00239             $this->list->title,
00240             $date,
00241             array(),
00242             array(
00243                 'oldid' => $this->revision->getId(),
00244                 'unhide' => 1
00245             )
00246         );
00247     }
00248 
00254     protected function getDiffLink() {
00255         if ( $this->isDeleted() && !$this->canViewContent() ) {
00256             return $this->list->msg( 'diff' )->escaped();
00257         } else {
00258             return Linker::linkKnown(
00259                     $this->list->title,
00260                     $this->list->msg( 'diff' )->escaped(),
00261                     array(),
00262                     array(
00263                         'diff' => $this->revision->getId(),
00264                         'oldid' => 'prev',
00265                         'unhide' => 1
00266                     )
00267                 );
00268         }
00269     }
00270 
00271     public function getHTML() {
00272         $difflink = $this->list->msg( 'parentheses' )
00273             ->rawParams( $this->getDiffLink() )->escaped();
00274         $revlink = $this->getRevisionLink();
00275         $userlink = Linker::revUserLink( $this->revision );
00276         $comment = Linker::revComment( $this->revision );
00277         if ( $this->isDeleted() ) {
00278             $revlink = "<span class=\"history-deleted\">$revlink</span>";
00279         }
00280         return "<li>$difflink $revlink $userlink $comment</li>";
00281     }
00282 }
00283 
00287 class RevDel_ArchiveList extends RevDel_RevisionList {
00288     public function getType() {
00289         return 'archive';
00290     }
00291 
00292     public static function getRelationType() {
00293         return 'ar_timestamp';
00294     }
00295 
00300     public function doQuery( $db ) {
00301         $timestamps = array();
00302         foreach ( $this->ids as $id ) {
00303             $timestamps[] = $db->timestamp( $id );
00304         }
00305         return $db->select( 'archive', '*',
00306                 array(
00307                     'ar_namespace' => $this->title->getNamespace(),
00308                     'ar_title' => $this->title->getDBkey(),
00309                     'ar_timestamp' => $timestamps
00310                 ),
00311                 __METHOD__,
00312                 array( 'ORDER BY' => 'ar_timestamp DESC' )
00313             );
00314     }
00315 
00316     public function newItem( $row ) {
00317         return new RevDel_ArchiveItem( $this, $row );
00318     }
00319 
00320     public function doPreCommitUpdates() {
00321         return Status::newGood();
00322     }
00323 
00324     public function doPostCommitUpdates() {
00325         return Status::newGood();
00326     }
00327 }
00328 
00332 class RevDel_ArchiveItem extends RevDel_RevisionItem {
00333     public function __construct( $list, $row ) {
00334         RevDel_Item::__construct( $list, $row );
00335         $this->revision = Revision::newFromArchiveRow( $row,
00336             array( 'page' => $this->list->title->getArticleID() ) );
00337     }
00338 
00339     public function getIdField() {
00340         return 'ar_timestamp';
00341     }
00342 
00343     public function getTimestampField() {
00344         return 'ar_timestamp';
00345     }
00346 
00347     public function getAuthorIdField() {
00348         return 'ar_user';
00349     }
00350 
00351     public function getAuthorNameField() {
00352         return 'ar_user_text';
00353     }
00354 
00355     public function getId() {
00356         # Convert DB timestamp to MW timestamp
00357         return $this->revision->getTimestamp();
00358     }
00359 
00360     public function setBits( $bits ) {
00361         $dbw = wfGetDB( DB_MASTER );
00362         $dbw->update( 'archive',
00363             array( 'ar_deleted' => $bits ),
00364             array(
00365                 'ar_namespace' => $this->list->title->getNamespace(),
00366                 'ar_title' => $this->list->title->getDBkey(),
00367                 // use timestamp for index
00368                 'ar_timestamp' => $this->row->ar_timestamp,
00369                 'ar_rev_id' => $this->row->ar_rev_id,
00370                 'ar_deleted' => $this->getBits()
00371             ),
00372             __METHOD__ );
00373         return (bool)$dbw->affectedRows();
00374     }
00375 
00376     protected function getRevisionLink() {
00377         $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate(
00378             $this->revision->getTimestamp(), $this->list->getUser() ) );
00379 
00380         if ( $this->isDeleted() && !$this->canViewContent() ) {
00381             return $date;
00382         }
00383 
00384         return Linker::link(
00385             SpecialPage::getTitleFor( 'Undelete' ),
00386             $date,
00387             array(),
00388             array(
00389                 'target' => $this->list->title->getPrefixedText(),
00390                 'timestamp' => $this->revision->getTimestamp()
00391             )
00392         );
00393     }
00394 
00395     protected function getDiffLink() {
00396         if ( $this->isDeleted() && !$this->canViewContent() ) {
00397             return $this->list->msg( 'diff' )->escaped();
00398         }
00399 
00400         return Linker::link(
00401             SpecialPage::getTitleFor( 'Undelete' ),
00402             $this->list->msg( 'diff' )->escaped(),
00403             array(),
00404             array(
00405                 'target' => $this->list->title->getPrefixedText(),
00406                 'diff' => 'prev',
00407                 'timestamp' => $this->revision->getTimestamp()
00408             )
00409         );
00410     }
00411 }
00412 
00417 class RevDel_ArchivedRevisionItem extends RevDel_ArchiveItem {
00418     public function __construct( $list, $row ) {
00419         RevDel_Item::__construct( $list, $row );
00420 
00421         $this->revision = Revision::newFromArchiveRow( $row,
00422             array( 'page' => $this->list->title->getArticleID() ) );
00423     }
00424 
00425     public function getIdField() {
00426         return 'ar_rev_id';
00427     }
00428 
00429     public function getId() {
00430         return $this->revision->getId();
00431     }
00432 
00433     public function setBits( $bits ) {
00434         $dbw = wfGetDB( DB_MASTER );
00435         $dbw->update( 'archive',
00436             array( 'ar_deleted' => $bits ),
00437             array( 'ar_rev_id' => $this->row->ar_rev_id,
00438                 'ar_deleted' => $this->getBits()
00439             ),
00440             __METHOD__ );
00441         return (bool)$dbw->affectedRows();
00442     }
00443 }
00444 
00448 class RevDel_FileList extends RevDel_List {
00449     public function getType() {
00450         return 'oldimage';
00451     }
00452 
00453     public static function getRelationType() {
00454         return 'oi_archive_name';
00455     }
00456 
00457     public static function getRestriction() {
00458         return 'deleterevision';
00459     }
00460 
00461     public static function getRevdelConstant() {
00462         return File::DELETED_FILE;
00463     }
00464 
00465     var $storeBatch, $deleteBatch, $cleanupBatch;
00466 
00471     public function doQuery( $db ) {
00472         $archiveNames = array();
00473         foreach ( $this->ids as $timestamp ) {
00474             $archiveNames[] = $timestamp . '!' . $this->title->getDBkey();
00475         }
00476         return $db->select(
00477             'oldimage',
00478             OldLocalFile::selectFields(),
00479             array(
00480                 'oi_name' => $this->title->getDBkey(),
00481                 'oi_archive_name' => $archiveNames
00482             ),
00483             __METHOD__,
00484             array( 'ORDER BY' => 'oi_timestamp DESC' )
00485         );
00486     }
00487 
00488     public function newItem( $row ) {
00489         return new RevDel_FileItem( $this, $row );
00490     }
00491 
00492     public function clearFileOps() {
00493         $this->deleteBatch = array();
00494         $this->storeBatch = array();
00495         $this->cleanupBatch = array();
00496     }
00497 
00498     public function doPreCommitUpdates() {
00499         $status = Status::newGood();
00500         $repo = RepoGroup::singleton()->getLocalRepo();
00501         if ( $this->storeBatch ) {
00502             $status->merge( $repo->storeBatch( $this->storeBatch, FileRepo::OVERWRITE_SAME ) );
00503         }
00504         if ( !$status->isOK() ) {
00505             return $status;
00506         }
00507         if ( $this->deleteBatch ) {
00508             $status->merge( $repo->deleteBatch( $this->deleteBatch ) );
00509         }
00510         if ( !$status->isOK() ) {
00511             // Running cleanupDeletedBatch() after a failed storeBatch() with the DB already
00512             // modified (but destined for rollback) causes data loss
00513             return $status;
00514         }
00515         if ( $this->cleanupBatch ) {
00516             $status->merge( $repo->cleanupDeletedBatch( $this->cleanupBatch ) );
00517         }
00518         return $status;
00519     }
00520 
00521     public function doPostCommitUpdates() {
00522         global $wgUseSquid;
00523         $file = wfLocalFile( $this->title );
00524         $file->purgeCache();
00525         $file->purgeDescription();
00526         $purgeUrls = array();
00527         foreach ( $this->ids as $timestamp ) {
00528             $archiveName = $timestamp . '!' . $this->title->getDBkey();
00529             $file->purgeOldThumbnails( $archiveName );
00530             $purgeUrls[] = $file->getArchiveUrl( $archiveName );
00531         }
00532         if ( $wgUseSquid ) {
00533             // purge full images from cache
00534             SquidUpdate::purge( $purgeUrls );
00535         }
00536         return Status::newGood();
00537     }
00538 
00539     public function getSuppressBit() {
00540         return File::DELETED_RESTRICTED;
00541     }
00542 }
00543 
00547 class RevDel_FileItem extends RevDel_Item {
00548 
00552     var $file;
00553 
00554     public function __construct( $list, $row ) {
00555         parent::__construct( $list, $row );
00556         $this->file = RepoGroup::singleton()->getLocalRepo()->newFileFromRow( $row );
00557     }
00558 
00559     public function getIdField() {
00560         return 'oi_archive_name';
00561     }
00562 
00563     public function getTimestampField() {
00564         return 'oi_timestamp';
00565     }
00566 
00567     public function getAuthorIdField() {
00568         return 'oi_user';
00569     }
00570 
00571     public function getAuthorNameField() {
00572         return 'oi_user_text';
00573     }
00574 
00575     public function getId() {
00576         $parts = explode( '!', $this->row->oi_archive_name );
00577         return $parts[0];
00578     }
00579 
00580     public function canView() {
00581         return $this->file->userCan( File::DELETED_RESTRICTED, $this->list->getUser() );
00582     }
00583 
00584     public function canViewContent() {
00585         return $this->file->userCan( File::DELETED_FILE, $this->list->getUser() );
00586     }
00587 
00588     public function getBits() {
00589         return $this->file->getVisibility();
00590     }
00591 
00592     public function setBits( $bits ) {
00593         # Queue the file op
00594         # @todo FIXME: Move to LocalFile.php
00595         if ( $this->isDeleted() ) {
00596             if ( $bits & File::DELETED_FILE ) {
00597                 # Still deleted
00598             } else {
00599                 # Newly undeleted
00600                 $key = $this->file->getStorageKey();
00601                 $srcRel = $this->file->repo->getDeletedHashPath( $key ) . $key;
00602                 $this->list->storeBatch[] = array(
00603                     $this->file->repo->getVirtualUrl( 'deleted' ) . '/' . $srcRel,
00604                     'public',
00605                     $this->file->getRel()
00606                 );
00607                 $this->list->cleanupBatch[] = $key;
00608             }
00609         } elseif ( $bits & File::DELETED_FILE ) {
00610             # Newly deleted
00611             $key = $this->file->getStorageKey();
00612             $dstRel = $this->file->repo->getDeletedHashPath( $key ) . $key;
00613             $this->list->deleteBatch[] = array( $this->file->getRel(), $dstRel );
00614         }
00615 
00616         # Do the database operations
00617         $dbw = wfGetDB( DB_MASTER );
00618         $dbw->update( 'oldimage',
00619             array( 'oi_deleted' => $bits ),
00620             array(
00621                 'oi_name' => $this->row->oi_name,
00622                 'oi_timestamp' => $this->row->oi_timestamp,
00623                 'oi_deleted' => $this->getBits()
00624             ),
00625             __METHOD__
00626         );
00627         return (bool)$dbw->affectedRows();
00628     }
00629 
00630     public function isDeleted() {
00631         return $this->file->isDeleted( File::DELETED_FILE );
00632     }
00633 
00639     protected function getLink() {
00640         $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate(
00641             $this->file->getTimestamp(), $this->list->getUser() ) );
00642 
00643         if ( !$this->isDeleted() ) {
00644             # Regular files...
00645             return Html::rawElement( 'a', array( 'href' => $this->file->getUrl() ), $date );
00646         }
00647 
00648         # Hidden files...
00649         if ( !$this->canViewContent() ) {
00650             $link = $date;
00651         } else {
00652             $link = Linker::link(
00653                 SpecialPage::getTitleFor( 'Revisiondelete' ),
00654                 $date,
00655                 array(),
00656                 array(
00657                     'target' => $this->list->title->getPrefixedText(),
00658                     'file' => $this->file->getArchiveName(),
00659                     'token' => $this->list->getUser()->getEditToken(
00660                         $this->file->getArchiveName() )
00661                 )
00662             );
00663         }
00664         return '<span class="history-deleted">' . $link . '</span>';
00665     }
00670     protected function getUserTools() {
00671         if ( $this->file->userCan( Revision::DELETED_USER, $this->list->getUser() ) ) {
00672             $link = Linker::userLink( $this->file->user, $this->file->user_text ) .
00673                 Linker::userToolLinks( $this->file->user, $this->file->user_text );
00674         } else {
00675             $link = $this->list->msg( 'rev-deleted-user' )->escaped();
00676         }
00677         if ( $this->file->isDeleted( Revision::DELETED_USER ) ) {
00678             return '<span class="history-deleted">' . $link . '</span>';
00679         }
00680         return $link;
00681     }
00682 
00689     protected function getComment() {
00690         if ( $this->file->userCan( File::DELETED_COMMENT, $this->list->getUser() ) ) {
00691             $block = Linker::commentBlock( $this->file->description );
00692         } else {
00693             $block = ' ' . $this->list->msg( 'rev-deleted-comment' )->escaped();
00694         }
00695         if ( $this->file->isDeleted( File::DELETED_COMMENT ) ) {
00696             return "<span class=\"history-deleted\">$block</span>";
00697         }
00698         return $block;
00699     }
00700 
00701     public function getHTML() {
00702         $data =
00703             $this->list->msg( 'widthheight' )->numParams(
00704                 $this->file->getWidth(), $this->file->getHeight() )->text() .
00705             ' (' . $this->list->msg( 'nbytes' )->numParams( $this->file->getSize() )->text() . ')';
00706 
00707         return '<li>' . $this->getLink() . ' ' . $this->getUserTools() . ' ' .
00708             $data . ' ' . $this->getComment() . '</li>';
00709     }
00710 }
00711 
00715 class RevDel_ArchivedFileList extends RevDel_FileList {
00716     public function getType() {
00717         return 'filearchive';
00718     }
00719 
00720     public static function getRelationType() {
00721         return 'fa_id';
00722     }
00723 
00728     public function doQuery( $db ) {
00729         $ids = array_map( 'intval', $this->ids );
00730         return $db->select(
00731             'filearchive',
00732             ArchivedFile::selectFields(),
00733             array(
00734                 'fa_name' => $this->title->getDBkey(),
00735                 'fa_id' => $ids
00736             ),
00737             __METHOD__,
00738             array( 'ORDER BY' => 'fa_id DESC' )
00739         );
00740     }
00741 
00742     public function newItem( $row ) {
00743         return new RevDel_ArchivedFileItem( $this, $row );
00744     }
00745 }
00746 
00750 class RevDel_ArchivedFileItem extends RevDel_FileItem {
00751     public function __construct( $list, $row ) {
00752         RevDel_Item::__construct( $list, $row );
00753         $this->file = ArchivedFile::newFromRow( $row );
00754     }
00755 
00756     public function getIdField() {
00757         return 'fa_id';
00758     }
00759 
00760     public function getTimestampField() {
00761         return 'fa_timestamp';
00762     }
00763 
00764     public function getAuthorIdField() {
00765         return 'fa_user';
00766     }
00767 
00768     public function getAuthorNameField() {
00769         return 'fa_user_text';
00770     }
00771 
00772     public function getId() {
00773         return $this->row->fa_id;
00774     }
00775 
00776     public function setBits( $bits ) {
00777         $dbw = wfGetDB( DB_MASTER );
00778         $dbw->update( 'filearchive',
00779             array( 'fa_deleted' => $bits ),
00780             array(
00781                 'fa_id' => $this->row->fa_id,
00782                 'fa_deleted' => $this->getBits(),
00783             ),
00784             __METHOD__
00785         );
00786         return (bool)$dbw->affectedRows();
00787     }
00788 
00789     protected function getLink() {
00790         $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate(
00791             $this->file->getTimestamp(), $this->list->getUser() ) );
00792 
00793         # Hidden files...
00794         if ( !$this->canViewContent() ) {
00795             $link = $date;
00796         } else {
00797             $undelete = SpecialPage::getTitleFor( 'Undelete' );
00798             $key = $this->file->getKey();
00799             $link = Linker::link( $undelete, $date, array(),
00800                 array(
00801                     'target' => $this->list->title->getPrefixedText(),
00802                     'file' => $key,
00803                     'token' => $this->list->getUser()->getEditToken( $key )
00804                 )
00805             );
00806         }
00807         if ( $this->isDeleted() ) {
00808             $link = '<span class="history-deleted">' . $link . '</span>';
00809         }
00810         return $link;
00811     }
00812 }
00813 
00817 class RevDel_LogList extends RevDel_List {
00818     public function getType() {
00819         return 'logging';
00820     }
00821 
00822     public static function getRelationType() {
00823         return 'log_id';
00824     }
00825 
00826     public static function getRestriction() {
00827         return 'deletelogentry';
00828     }
00829 
00830     public static function getRevdelConstant() {
00831         return LogPage::DELETED_ACTION;
00832     }
00833 
00834     public static function suggestTarget( $target, array $ids ) {
00835         $result = wfGetDB( DB_SLAVE )->select( 'logging',
00836             'log_type',
00837             array( 'log_id' => $ids ),
00838             __METHOD__,
00839             array( 'DISTINCT' )
00840         );
00841         if ( $result->numRows() == 1 ) {
00842             // If there's only one type, the target can be set to include it.
00843             return SpecialPage::getTitleFor( 'Log', $result->current()->log_type );
00844         }
00845         return SpecialPage::getTitleFor( 'Log' );
00846     }
00847 
00852     public function doQuery( $db ) {
00853         $ids = array_map( 'intval', $this->ids );
00854         return $db->select( 'logging', '*',
00855             array( 'log_id' => $ids ),
00856             __METHOD__,
00857             array( 'ORDER BY' => 'log_id DESC' )
00858         );
00859     }
00860 
00861     public function newItem( $row ) {
00862         return new RevDel_LogItem( $this, $row );
00863     }
00864 
00865     public function getSuppressBit() {
00866         return Revision::DELETED_RESTRICTED;
00867     }
00868 
00869     public function getLogAction() {
00870         return 'event';
00871     }
00872 
00873     public function getLogParams( $params ) {
00874         return array(
00875             implode( ',', $params['ids'] ),
00876             "ofield={$params['oldBits']}",
00877             "nfield={$params['newBits']}"
00878         );
00879     }
00880 }
00881 
00885 class RevDel_LogItem extends RevDel_Item {
00886     public function getIdField() {
00887         return 'log_id';
00888     }
00889 
00890     public function getTimestampField() {
00891         return 'log_timestamp';
00892     }
00893 
00894     public function getAuthorIdField() {
00895         return 'log_user';
00896     }
00897 
00898     public function getAuthorNameField() {
00899         return 'log_user_text';
00900     }
00901 
00902     public function canView() {
00903         return LogEventsList::userCan( $this->row, Revision::DELETED_RESTRICTED, $this->list->getUser() );
00904     }
00905 
00906     public function canViewContent() {
00907         return true; // none
00908     }
00909 
00910     public function getBits() {
00911         return $this->row->log_deleted;
00912     }
00913 
00914     public function setBits( $bits ) {
00915         $dbw = wfGetDB( DB_MASTER );
00916         $dbw->update( 'recentchanges',
00917             array(
00918                 'rc_deleted' => $bits,
00919                 'rc_patrolled' => 1
00920             ),
00921             array(
00922                 'rc_logid' => $this->row->log_id,
00923                 'rc_timestamp' => $this->row->log_timestamp // index
00924             ),
00925             __METHOD__
00926         );
00927         $dbw->update( 'logging',
00928             array( 'log_deleted' => $bits ),
00929             array(
00930                 'log_id' => $this->row->log_id,
00931                 'log_deleted' => $this->getBits()
00932             ),
00933             __METHOD__
00934         );
00935         return (bool)$dbw->affectedRows();
00936     }
00937 
00938     public function getHTML() {
00939         $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate(
00940             $this->row->log_timestamp, $this->list->getUser() ) );
00941         $title = Title::makeTitle( $this->row->log_namespace, $this->row->log_title );
00942         $formatter = LogFormatter::newFromRow( $this->row );
00943         $formatter->setContext( $this->list->getContext() );
00944         $formatter->setAudience( LogFormatter::FOR_THIS_USER );
00945 
00946         // Log link for this page
00947         $loglink = Linker::link(
00948             SpecialPage::getTitleFor( 'Log' ),
00949             $this->list->msg( 'log' )->escaped(),
00950             array(),
00951             array( 'page' => $title->getPrefixedText() )
00952         );
00953         $loglink = $this->list->msg( 'parentheses' )->rawParams( $loglink )->escaped();
00954         // User links and action text
00955         $action = $formatter->getActionText();
00956         // Comment
00957         $comment = $this->list->getLanguage()->getDirMark() . Linker::commentBlock( $this->row->log_comment );
00958         if ( LogEventsList::isDeleted( $this->row, LogPage::DELETED_COMMENT ) ) {
00959             $comment = '<span class="history-deleted">' . $comment . '</span>';
00960         }
00961 
00962         return "<li>$loglink $date $action $comment</li>";
00963     }
00964 }