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