From c1f9b1f7b1b77776192048005dcc66dcf3df2bfb Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sat, 27 Dec 2014 15:41:37 +0100 Subject: Update to MediaWiki 1.24.1 --- includes/revisiondelete/RevDelArchiveItem.php | 105 +++ includes/revisiondelete/RevDelArchiveList.php | 66 ++ includes/revisiondelete/RevDelArchivedFileItem.php | 129 +++ includes/revisiondelete/RevDelArchivedFileList.php | 56 ++ .../revisiondelete/RevDelArchivedRevisionItem.php | 53 ++ includes/revisiondelete/RevDelFileItem.php | 237 +++++ includes/revisiondelete/RevDelFileList.php | 128 +++ includes/revisiondelete/RevDelItem.php | 62 ++ includes/revisiondelete/RevDelList.php | 313 +++++++ includes/revisiondelete/RevDelLogItem.php | 151 ++++ includes/revisiondelete/RevDelLogList.php | 103 +++ includes/revisiondelete/RevDelRevisionItem.php | 187 ++++ includes/revisiondelete/RevDelRevisionList.php | 143 +++ includes/revisiondelete/RevisionDelete.php | 964 --------------------- .../revisiondelete/RevisionDeleteAbstracts.php | 328 ------- includes/revisiondelete/RevisionDeleteUser.php | 38 +- includes/revisiondelete/RevisionDeleter.php | 37 +- 17 files changed, 1773 insertions(+), 1327 deletions(-) create mode 100644 includes/revisiondelete/RevDelArchiveItem.php create mode 100644 includes/revisiondelete/RevDelArchiveList.php create mode 100644 includes/revisiondelete/RevDelArchivedFileItem.php create mode 100644 includes/revisiondelete/RevDelArchivedFileList.php create mode 100644 includes/revisiondelete/RevDelArchivedRevisionItem.php create mode 100644 includes/revisiondelete/RevDelFileItem.php create mode 100644 includes/revisiondelete/RevDelFileList.php create mode 100644 includes/revisiondelete/RevDelItem.php create mode 100644 includes/revisiondelete/RevDelList.php create mode 100644 includes/revisiondelete/RevDelLogItem.php create mode 100644 includes/revisiondelete/RevDelLogList.php create mode 100644 includes/revisiondelete/RevDelRevisionItem.php create mode 100644 includes/revisiondelete/RevDelRevisionList.php delete mode 100644 includes/revisiondelete/RevisionDelete.php delete mode 100644 includes/revisiondelete/RevisionDeleteAbstracts.php (limited to 'includes/revisiondelete') diff --git a/includes/revisiondelete/RevDelArchiveItem.php b/includes/revisiondelete/RevDelArchiveItem.php new file mode 100644 index 00000000..0f1c7f0c --- /dev/null +++ b/includes/revisiondelete/RevDelArchiveItem.php @@ -0,0 +1,105 @@ +revision = Revision::newFromArchiveRow( $row, + array( 'page' => $this->list->title->getArticleID() ) ); + } + + public function getIdField() { + return 'ar_timestamp'; + } + + public function getTimestampField() { + return 'ar_timestamp'; + } + + public function getAuthorIdField() { + return 'ar_user'; + } + + public function getAuthorNameField() { + return 'ar_user_text'; + } + + public function getId() { + # Convert DB timestamp to MW timestamp + return $this->revision->getTimestamp(); + } + + public function setBits( $bits ) { + $dbw = wfGetDB( DB_MASTER ); + $dbw->update( 'archive', + array( 'ar_deleted' => $bits ), + array( + 'ar_namespace' => $this->list->title->getNamespace(), + 'ar_title' => $this->list->title->getDBkey(), + // use timestamp for index + 'ar_timestamp' => $this->row->ar_timestamp, + 'ar_rev_id' => $this->row->ar_rev_id, + 'ar_deleted' => $this->getBits() + ), + __METHOD__ ); + + return (bool)$dbw->affectedRows(); + } + + protected function getRevisionLink() { + $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate( + $this->revision->getTimestamp(), $this->list->getUser() ) ); + + if ( $this->isDeleted() && !$this->canViewContent() ) { + return $date; + } + + return Linker::link( + SpecialPage::getTitleFor( 'Undelete' ), + $date, + array(), + array( + 'target' => $this->list->title->getPrefixedText(), + 'timestamp' => $this->revision->getTimestamp() + ) + ); + } + + protected function getDiffLink() { + if ( $this->isDeleted() && !$this->canViewContent() ) { + return $this->list->msg( 'diff' )->escaped(); + } + + return Linker::link( + SpecialPage::getTitleFor( 'Undelete' ), + $this->list->msg( 'diff' )->escaped(), + array(), + array( + 'target' => $this->list->title->getPrefixedText(), + 'diff' => 'prev', + 'timestamp' => $this->revision->getTimestamp() + ) + ); + } +} diff --git a/includes/revisiondelete/RevDelArchiveList.php b/includes/revisiondelete/RevDelArchiveList.php new file mode 100644 index 00000000..e7aed737 --- /dev/null +++ b/includes/revisiondelete/RevDelArchiveList.php @@ -0,0 +1,66 @@ +ids as $id ) { + $timestamps[] = $db->timestamp( $id ); + } + + return $db->select( 'archive', Revision::selectArchiveFields(), + array( + 'ar_namespace' => $this->title->getNamespace(), + 'ar_title' => $this->title->getDBkey(), + 'ar_timestamp' => $timestamps + ), + __METHOD__, + array( 'ORDER BY' => 'ar_timestamp DESC' ) + ); + } + + public function newItem( $row ) { + return new RevDelArchiveItem( $this, $row ); + } + + public function doPreCommitUpdates() { + return Status::newGood(); + } + + public function doPostCommitUpdates() { + return Status::newGood(); + } +} diff --git a/includes/revisiondelete/RevDelArchivedFileItem.php b/includes/revisiondelete/RevDelArchivedFileItem.php new file mode 100644 index 00000000..7c41c180 --- /dev/null +++ b/includes/revisiondelete/RevDelArchivedFileItem.php @@ -0,0 +1,129 @@ +file = ArchivedFile::newFromRow( $row ); + } + + public function getIdField() { + return 'fa_id'; + } + + public function getTimestampField() { + return 'fa_timestamp'; + } + + public function getAuthorIdField() { + return 'fa_user'; + } + + public function getAuthorNameField() { + return 'fa_user_text'; + } + + public function getId() { + return $this->row->fa_id; + } + + public function setBits( $bits ) { + $dbw = wfGetDB( DB_MASTER ); + $dbw->update( 'filearchive', + array( 'fa_deleted' => $bits ), + array( + 'fa_id' => $this->row->fa_id, + 'fa_deleted' => $this->getBits(), + ), + __METHOD__ + ); + + return (bool)$dbw->affectedRows(); + } + + protected function getLink() { + $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate( + $this->file->getTimestamp(), $this->list->getUser() ) ); + + # Hidden files... + if ( !$this->canViewContent() ) { + $link = $date; + } else { + $undelete = SpecialPage::getTitleFor( 'Undelete' ); + $key = $this->file->getKey(); + $link = Linker::link( $undelete, $date, array(), + array( + 'target' => $this->list->title->getPrefixedText(), + 'file' => $key, + 'token' => $this->list->getUser()->getEditToken( $key ) + ) + ); + } + if ( $this->isDeleted() ) { + $link = '' . $link . ''; + } + + return $link; + } + + public function getApiData( ApiResult $result ) { + $file = $this->file; + $user = $this->list->getUser(); + $ret = array( + 'title' => $this->list->title->getPrefixedText(), + 'timestamp' => wfTimestamp( TS_ISO_8601, $file->getTimestamp() ), + 'width' => $file->getWidth(), + 'height' => $file->getHeight(), + 'size' => $file->getSize(), + ); + $ret += $file->isDeleted( Revision::DELETED_USER ) ? array( 'userhidden' => '' ) : array(); + $ret += $file->isDeleted( Revision::DELETED_COMMENT ) ? array( 'commenthidden' => '' ) : array(); + $ret += $this->isDeleted() ? array( 'contenthidden' => '' ) : array(); + if ( $this->canViewContent() ) { + $ret += array( + 'url' => SpecialPage::getTitleFor( 'Revisiondelete' )->getLinkURL( + array( + 'target' => $this->list->title->getPrefixedText(), + 'file' => $file->getKey(), + 'token' => $user->getEditToken( $file->getKey() ) + ), + false, PROTO_RELATIVE + ), + ); + } + if ( $file->userCan( Revision::DELETED_USER, $user ) ) { + $ret += array( + 'userid' => $file->getUser( 'id' ), + 'user' => $file->getUser( 'text' ), + ); + } + if ( $file->userCan( Revision::DELETED_COMMENT, $user ) ) { + $ret += array( + 'comment' => $file->getRawDescription(), + ); + } + + return $ret; + } +} diff --git a/includes/revisiondelete/RevDelArchivedFileList.php b/includes/revisiondelete/RevDelArchivedFileList.php new file mode 100644 index 00000000..aec51b17 --- /dev/null +++ b/includes/revisiondelete/RevDelArchivedFileList.php @@ -0,0 +1,56 @@ +ids ); + + return $db->select( + 'filearchive', + ArchivedFile::selectFields(), + array( + 'fa_name' => $this->title->getDBkey(), + 'fa_id' => $ids + ), + __METHOD__, + array( 'ORDER BY' => 'fa_id DESC' ) + ); + } + + public function newItem( $row ) { + return new RevDelArchivedFileItem( $this, $row ); + } +} diff --git a/includes/revisiondelete/RevDelArchivedRevisionItem.php b/includes/revisiondelete/RevDelArchivedRevisionItem.php new file mode 100644 index 00000000..9ec548fb --- /dev/null +++ b/includes/revisiondelete/RevDelArchivedRevisionItem.php @@ -0,0 +1,53 @@ +revision = Revision::newFromArchiveRow( $row, + array( 'page' => $this->list->title->getArticleID() ) ); + } + + public function getIdField() { + return 'ar_rev_id'; + } + + public function getId() { + return $this->revision->getId(); + } + + public function setBits( $bits ) { + $dbw = wfGetDB( DB_MASTER ); + $dbw->update( 'archive', + array( 'ar_deleted' => $bits ), + array( 'ar_rev_id' => $this->row->ar_rev_id, + 'ar_deleted' => $this->getBits() + ), + __METHOD__ ); + + return (bool)$dbw->affectedRows(); + } +} diff --git a/includes/revisiondelete/RevDelFileItem.php b/includes/revisiondelete/RevDelFileItem.php new file mode 100644 index 00000000..a6517fe9 --- /dev/null +++ b/includes/revisiondelete/RevDelFileItem.php @@ -0,0 +1,237 @@ +file = RepoGroup::singleton()->getLocalRepo()->newFileFromRow( $row ); + } + + public function getIdField() { + return 'oi_archive_name'; + } + + public function getTimestampField() { + return 'oi_timestamp'; + } + + public function getAuthorIdField() { + return 'oi_user'; + } + + public function getAuthorNameField() { + return 'oi_user_text'; + } + + public function getId() { + $parts = explode( '!', $this->row->oi_archive_name ); + + return $parts[0]; + } + + public function canView() { + return $this->file->userCan( File::DELETED_RESTRICTED, $this->list->getUser() ); + } + + public function canViewContent() { + return $this->file->userCan( File::DELETED_FILE, $this->list->getUser() ); + } + + public function getBits() { + return $this->file->getVisibility(); + } + + public function setBits( $bits ) { + # Queue the file op + # @todo FIXME: Move to LocalFile.php + if ( $this->isDeleted() ) { + if ( $bits & File::DELETED_FILE ) { + # Still deleted + } else { + # Newly undeleted + $key = $this->file->getStorageKey(); + $srcRel = $this->file->repo->getDeletedHashPath( $key ) . $key; + $this->list->storeBatch[] = array( + $this->file->repo->getVirtualUrl( 'deleted' ) . '/' . $srcRel, + 'public', + $this->file->getRel() + ); + $this->list->cleanupBatch[] = $key; + } + } elseif ( $bits & File::DELETED_FILE ) { + # Newly deleted + $key = $this->file->getStorageKey(); + $dstRel = $this->file->repo->getDeletedHashPath( $key ) . $key; + $this->list->deleteBatch[] = array( $this->file->getRel(), $dstRel ); + } + + # Do the database operations + $dbw = wfGetDB( DB_MASTER ); + $dbw->update( 'oldimage', + array( 'oi_deleted' => $bits ), + array( + 'oi_name' => $this->row->oi_name, + 'oi_timestamp' => $this->row->oi_timestamp, + 'oi_deleted' => $this->getBits() + ), + __METHOD__ + ); + + return (bool)$dbw->affectedRows(); + } + + public function isDeleted() { + return $this->file->isDeleted( File::DELETED_FILE ); + } + + /** + * Get the link to the file. + * Overridden by RevDelArchivedFileItem. + * @return string + */ + protected function getLink() { + $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate( + $this->file->getTimestamp(), $this->list->getUser() ) ); + + if ( !$this->isDeleted() ) { + # Regular files... + return Html::rawElement( 'a', array( 'href' => $this->file->getUrl() ), $date ); + } + + # Hidden files... + if ( !$this->canViewContent() ) { + $link = $date; + } else { + $link = Linker::link( + SpecialPage::getTitleFor( 'Revisiondelete' ), + $date, + array(), + array( + 'target' => $this->list->title->getPrefixedText(), + 'file' => $this->file->getArchiveName(), + 'token' => $this->list->getUser()->getEditToken( + $this->file->getArchiveName() ) + ) + ); + } + + return '' . $link . ''; + } + + /** + * Generate a user tool link cluster if the current user is allowed to view it + * @return string HTML + */ + protected function getUserTools() { + if ( $this->file->userCan( Revision::DELETED_USER, $this->list->getUser() ) ) { + $uid = $this->file->getUser( 'id' ); + $name = $this->file->getUser( 'text' ); + $link = Linker::userLink( $uid, $name ) . Linker::userToolLinks( $uid, $name ); + } else { + $link = $this->list->msg( 'rev-deleted-user' )->escaped(); + } + if ( $this->file->isDeleted( Revision::DELETED_USER ) ) { + return '' . $link . ''; + } + + return $link; + } + + /** + * Wrap and format the file's comment block, if the current + * user is allowed to view it. + * + * @return string HTML + */ + protected function getComment() { + if ( $this->file->userCan( File::DELETED_COMMENT, $this->list->getUser() ) ) { + $block = Linker::commentBlock( $this->file->getDescription() ); + } else { + $block = ' ' . $this->list->msg( 'rev-deleted-comment' )->escaped(); + } + if ( $this->file->isDeleted( File::DELETED_COMMENT ) ) { + return "$block"; + } + + return $block; + } + + public function getHTML() { + $data = + $this->list->msg( 'widthheight' )->numParams( + $this->file->getWidth(), $this->file->getHeight() )->text() . + ' (' . $this->list->msg( 'nbytes' )->numParams( $this->file->getSize() )->text() . ')'; + + return '
  • ' . $this->getLink() . ' ' . $this->getUserTools() . ' ' . + $data . ' ' . $this->getComment() . '
  • '; + } + + public function getApiData( ApiResult $result ) { + $file = $this->file; + $user = $this->list->getUser(); + $ret = array( + 'title' => $this->list->title->getPrefixedText(), + 'archivename' => $file->getArchiveName(), + 'timestamp' => wfTimestamp( TS_ISO_8601, $file->getTimestamp() ), + 'width' => $file->getWidth(), + 'height' => $file->getHeight(), + 'size' => $file->getSize(), + ); + $ret += $file->isDeleted( Revision::DELETED_USER ) ? array( 'userhidden' => '' ) : array(); + $ret += $file->isDeleted( Revision::DELETED_COMMENT ) ? array( 'commenthidden' => '' ) : array(); + $ret += $this->isDeleted() ? array( 'contenthidden' => '' ) : array(); + if ( !$this->isDeleted() ) { + $ret += array( + 'url' => $file->getUrl(), + ); + } elseif ( $this->canViewContent() ) { + $ret += array( + 'url' => SpecialPage::getTitleFor( 'Revisiondelete' )->getLinkURL( + array( + 'target' => $this->list->title->getPrefixedText(), + 'file' => $file->getArchiveName(), + 'token' => $user->getEditToken( $file->getArchiveName() ) + ), + false, PROTO_RELATIVE + ), + ); + } + if ( $file->userCan( Revision::DELETED_USER, $user ) ) { + $ret += array( + 'userid' => $file->user, + 'user' => $file->user_text, + ); + } + if ( $file->userCan( Revision::DELETED_COMMENT, $user ) ) { + $ret += array( + 'comment' => $file->description, + ); + } + + return $ret; + } +} diff --git a/includes/revisiondelete/RevDelFileList.php b/includes/revisiondelete/RevDelFileList.php new file mode 100644 index 00000000..57e15d81 --- /dev/null +++ b/includes/revisiondelete/RevDelFileList.php @@ -0,0 +1,128 @@ +ids as $timestamp ) { + $archiveNames[] = $timestamp . '!' . $this->title->getDBkey(); + } + + return $db->select( + 'oldimage', + OldLocalFile::selectFields(), + array( + 'oi_name' => $this->title->getDBkey(), + 'oi_archive_name' => $archiveNames + ), + __METHOD__, + array( 'ORDER BY' => 'oi_timestamp DESC' ) + ); + } + + public function newItem( $row ) { + return new RevDelFileItem( $this, $row ); + } + + public function clearFileOps() { + $this->deleteBatch = array(); + $this->storeBatch = array(); + $this->cleanupBatch = array(); + } + + public function doPreCommitUpdates() { + $status = Status::newGood(); + $repo = RepoGroup::singleton()->getLocalRepo(); + if ( $this->storeBatch ) { + $status->merge( $repo->storeBatch( $this->storeBatch, FileRepo::OVERWRITE_SAME ) ); + } + if ( !$status->isOK() ) { + return $status; + } + if ( $this->deleteBatch ) { + $status->merge( $repo->deleteBatch( $this->deleteBatch ) ); + } + if ( !$status->isOK() ) { + // Running cleanupDeletedBatch() after a failed storeBatch() with the DB already + // modified (but destined for rollback) causes data loss + return $status; + } + if ( $this->cleanupBatch ) { + $status->merge( $repo->cleanupDeletedBatch( $this->cleanupBatch ) ); + } + + return $status; + } + + public function doPostCommitUpdates() { + $file = wfLocalFile( $this->title ); + $file->purgeCache(); + $file->purgeDescription(); + $purgeUrls = array(); + foreach ( $this->ids as $timestamp ) { + $archiveName = $timestamp . '!' . $this->title->getDBkey(); + $file->purgeOldThumbnails( $archiveName ); + $purgeUrls[] = $file->getArchiveUrl( $archiveName ); + } + if ( $this->getConfig()->get( 'UseSquid' ) ) { + // purge full images from cache + SquidUpdate::purge( $purgeUrls ); + } + + return Status::newGood(); + } + + public function getSuppressBit() { + return File::DELETED_RESTRICTED; + } +} diff --git a/includes/revisiondelete/RevDelItem.php b/includes/revisiondelete/RevDelItem.php new file mode 100644 index 00000000..ebdbf3a8 --- /dev/null +++ b/includes/revisiondelete/RevDelItem.php @@ -0,0 +1,62 @@ +ids = $ids; + } + + /** + * Get the DB field name associated with the ID list. + * This used to populate the log_search table for finding log entries. + * Override this function. + * @return string|null + */ + public static function getRelationType() { + return null; + } + + /** + * Get the user right required for this list type + * Override this function. + * @since 1.22 + * @return string|null + */ + public static function getRestriction() { + return null; + } + + /** + * Get the revision deletion constant for this list type + * Override this function. + * @since 1.22 + * @return int|null + */ + public static function getRevdelConstant() { + return null; + } + + /** + * Suggest a target for the revision deletion + * Optionally override this function. + * @since 1.22 + * @param Title|null $target User-supplied target + * @param array $ids + * @return Title|null + */ + public static function suggestTarget( $target, array $ids ) { + return $target; + } + + /** + * Set the visibility for the revisions in this list. Logging and + * transactions are done here. + * + * @param array $params Associative array of parameters. Members are: + * value: The integer value to set the visibility to + * comment: The log comment. + * perItemStatus: Set if you want per-item status reports + * @return Status + * @since 1.23 Added 'perItemStatus' param + */ + public function setVisibility( $params ) { + $bitPars = $params['value']; + $comment = $params['comment']; + $perItemStatus = isset( $params['perItemStatus'] ) ? $params['perItemStatus'] : false; + + $this->res = false; + $dbw = wfGetDB( DB_MASTER ); + $this->doQuery( $dbw ); + $dbw->begin( __METHOD__ ); + $status = Status::newGood(); + $missing = array_flip( $this->ids ); + $this->clearFileOps(); + $idsForLog = array(); + $authorIds = $authorIPs = array(); + + if ( $perItemStatus ) { + $status->itemStatuses = array(); + } + + // @codingStandardsIgnoreStart Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed + for ( $this->reset(); $this->current(); $this->next() ) { + // @codingStandardsIgnoreEnd + $item = $this->current(); + unset( $missing[$item->getId()] ); + + if ( $perItemStatus ) { + $itemStatus = Status::newGood(); + $status->itemStatuses[$item->getId()] = $itemStatus; + } else { + $itemStatus = $status; + } + + $oldBits = $item->getBits(); + // Build the actual new rev_deleted bitfield + $newBits = RevisionDeleter::extractBitfield( $bitPars, $oldBits ); + + if ( $oldBits == $newBits ) { + $itemStatus->warning( 'revdelete-no-change', $item->formatDate(), $item->formatTime() ); + $status->failCount++; + continue; + } elseif ( $oldBits == 0 && $newBits != 0 ) { + $opType = 'hide'; + } elseif ( $oldBits != 0 && $newBits == 0 ) { + $opType = 'show'; + } else { + $opType = 'modify'; + } + + if ( $item->isHideCurrentOp( $newBits ) ) { + // Cannot hide current version text + $itemStatus->error( 'revdelete-hide-current', $item->formatDate(), $item->formatTime() ); + $status->failCount++; + continue; + } + if ( !$item->canView() ) { + // Cannot access this revision + $msg = ( $opType == 'show' ) ? + 'revdelete-show-no-access' : 'revdelete-modify-no-access'; + $itemStatus->error( $msg, $item->formatDate(), $item->formatTime() ); + $status->failCount++; + continue; + } + // Cannot just "hide from Sysops" without hiding any fields + if ( $newBits == Revision::DELETED_RESTRICTED ) { + $itemStatus->warning( 'revdelete-only-restricted', $item->formatDate(), $item->formatTime() ); + $status->failCount++; + continue; + } + + // Update the revision + $ok = $item->setBits( $newBits ); + + if ( $ok ) { + $idsForLog[] = $item->getId(); + $status->successCount++; + if ( $item->getAuthorId() > 0 ) { + $authorIds[] = $item->getAuthorId(); + } elseif ( IP::isIPAddress( $item->getAuthorName() ) ) { + $authorIPs[] = $item->getAuthorName(); + } + } else { + $itemStatus->error( 'revdelete-concurrent-change', $item->formatDate(), $item->formatTime() ); + $status->failCount++; + } + } + + // Handle missing revisions + foreach ( $missing as $id => $unused ) { + if ( $perItemStatus ) { + $status->itemStatuses[$id] = Status::newFatal( 'revdelete-modify-missing', $id ); + } else { + $status->error( 'revdelete-modify-missing', $id ); + } + $status->failCount++; + } + + if ( $status->successCount == 0 ) { + $dbw->rollback( __METHOD__ ); + return $status; + } + + // Save success count + $successCount = $status->successCount; + + // Move files, if there are any + $status->merge( $this->doPreCommitUpdates() ); + if ( !$status->isOK() ) { + // Fatal error, such as no configured archive directory + $dbw->rollback( __METHOD__ ); + return $status; + } + + // Log it + $this->updateLog( array( + 'title' => $this->title, + 'count' => $successCount, + 'newBits' => $newBits, + 'oldBits' => $oldBits, + 'comment' => $comment, + 'ids' => $idsForLog, + 'authorIds' => $authorIds, + 'authorIPs' => $authorIPs + ) ); + $dbw->commit( __METHOD__ ); + + // Clear caches + $status->merge( $this->doPostCommitUpdates() ); + return $status; + } + + /** + * Reload the list data from the master DB. This can be done after setVisibility() + * to allow $item->getHTML() to show the new data. + */ + function reloadFromMaster() { + $dbw = wfGetDB( DB_MASTER ); + $this->res = $this->doQuery( $dbw ); + } + + /** + * Record a log entry on the action + * @param array $params Associative array of parameters: + * newBits: The new value of the *_deleted bitfield + * oldBits: The old value of the *_deleted bitfield. + * title: The target title + * ids: The ID list + * comment: The log comment + * authorsIds: The array of the user IDs of the offenders + * authorsIPs: The array of the IP/anon user offenders + * @throws MWException + */ + protected function updateLog( $params ) { + // Get the URL param's corresponding DB field + $field = RevisionDeleter::getRelationType( $this->getType() ); + if ( !$field ) { + throw new MWException( "Bad log URL param type!" ); + } + // Put things hidden from sysops in the oversight log + if ( ( $params['newBits'] | $params['oldBits'] ) & $this->getSuppressBit() ) { + $logType = 'suppress'; + } else { + $logType = 'delete'; + } + // Add params for effected page and ids + $logParams = $this->getLogParams( $params ); + // Actually add the deletion log entry + $log = new LogPage( $logType ); + $logid = $log->addEntry( $this->getLogAction(), $params['title'], + $params['comment'], $logParams, $this->getUser() ); + // Allow for easy searching of deletion log items for revision/log items + $log->addRelations( $field, $params['ids'], $logid ); + $log->addRelations( 'target_author_id', $params['authorIds'], $logid ); + $log->addRelations( 'target_author_ip', $params['authorIPs'], $logid ); + } + + /** + * Get the log action for this list type + * @return string + */ + public function getLogAction() { + return 'revision'; + } + + /** + * Get log parameter array. + * @param array $params Associative array of log parameters, same as updateLog() + * @return array + */ + public function getLogParams( $params ) { + return array( + $this->getType(), + implode( ',', $params['ids'] ), + "ofield={$params['oldBits']}", + "nfield={$params['newBits']}" + ); + } + + /** + * Clear any data structures needed for doPreCommitUpdates() and doPostCommitUpdates() + * STUB + */ + public function clearFileOps() { + } + + /** + * A hook for setVisibility(): do batch updates pre-commit. + * STUB + * @return Status + */ + public function doPreCommitUpdates() { + return Status::newGood(); + } + + /** + * A hook for setVisibility(): do any necessary updates post-commit. + * STUB + * @return Status + */ + public function doPostCommitUpdates() { + return Status::newGood(); + } + + /** + * Get the integer value of the flag used for suppression + */ + abstract public function getSuppressBit(); +} diff --git a/includes/revisiondelete/RevDelLogItem.php b/includes/revisiondelete/RevDelLogItem.php new file mode 100644 index 00000000..5c8b8c9d --- /dev/null +++ b/includes/revisiondelete/RevDelLogItem.php @@ -0,0 +1,151 @@ +row, Revision::DELETED_RESTRICTED, $this->list->getUser() ); + } + + public function canViewContent() { + return true; // none + } + + public function getBits() { + return $this->row->log_deleted; + } + + public function setBits( $bits ) { + $dbw = wfGetDB( DB_MASTER ); + $dbw->update( 'recentchanges', + array( + 'rc_deleted' => $bits, + 'rc_patrolled' => 1 + ), + array( + 'rc_logid' => $this->row->log_id, + 'rc_timestamp' => $this->row->log_timestamp // index + ), + __METHOD__ + ); + $dbw->update( 'logging', + array( 'log_deleted' => $bits ), + array( + 'log_id' => $this->row->log_id, + 'log_deleted' => $this->getBits() + ), + __METHOD__ + ); + + return (bool)$dbw->affectedRows(); + } + + public function getHTML() { + $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate( + $this->row->log_timestamp, $this->list->getUser() ) ); + $title = Title::makeTitle( $this->row->log_namespace, $this->row->log_title ); + $formatter = LogFormatter::newFromRow( $this->row ); + $formatter->setContext( $this->list->getContext() ); + $formatter->setAudience( LogFormatter::FOR_THIS_USER ); + + // Log link for this page + $loglink = Linker::link( + SpecialPage::getTitleFor( 'Log' ), + $this->list->msg( 'log' )->escaped(), + array(), + array( 'page' => $title->getPrefixedText() ) + ); + $loglink = $this->list->msg( 'parentheses' )->rawParams( $loglink )->escaped(); + // User links and action text + $action = $formatter->getActionText(); + // Comment + $comment = $this->list->getLanguage()->getDirMark() + . Linker::commentBlock( $this->row->log_comment ); + + if ( LogEventsList::isDeleted( $this->row, LogPage::DELETED_COMMENT ) ) { + $comment = '' . $comment . ''; + } + + return "
  • $loglink $date $action $comment
  • "; + } + + public function getApiData( ApiResult $result ) { + $logEntry = DatabaseLogEntry::newFromRow( $this->row ); + $user = $this->list->getUser(); + $ret = array( + 'id' => $logEntry->getId(), + 'type' => $logEntry->getType(), + 'action' => $logEntry->getSubtype(), + ); + $ret += $logEntry->isDeleted( LogPage::DELETED_USER ) + ? array( 'userhidden' => '' ) + : array(); + $ret += $logEntry->isDeleted( LogPage::DELETED_COMMENT ) + ? array( 'commenthidden' => '' ) + : array(); + $ret += $logEntry->isDeleted( LogPage::DELETED_ACTION ) + ? array( 'actionhidden' => '' ) + : array(); + + if ( LogEventsList::userCan( $this->row, LogPage::DELETED_ACTION, $user ) ) { + ApiQueryLogEvents::addLogParams( + $result, + $ret, + $logEntry->getParameters(), + $logEntry->getType(), + $logEntry->getSubtype(), + $logEntry->getTimestamp(), + $logEntry->isLegacy() + ); + } + if ( LogEventsList::userCan( $this->row, LogPage::DELETED_USER, $user ) ) { + $ret += array( + 'userid' => $this->row->log_user, + 'user' => $this->row->log_user_text, + ); + } + if ( LogEventsList::userCan( $this->row, LogPage::DELETED_COMMENT, $user ) ) { + $ret += array( + 'comment' => $this->row->log_comment, + ); + } + + return $ret; + } +} diff --git a/includes/revisiondelete/RevDelLogList.php b/includes/revisiondelete/RevDelLogList.php new file mode 100644 index 00000000..ad040425 --- /dev/null +++ b/includes/revisiondelete/RevDelLogList.php @@ -0,0 +1,103 @@ +select( 'logging', + 'log_type', + array( 'log_id' => $ids ), + __METHOD__, + array( 'DISTINCT' ) + ); + if ( $result->numRows() == 1 ) { + // If there's only one type, the target can be set to include it. + return SpecialPage::getTitleFor( 'Log', $result->current()->log_type ); + } + + return SpecialPage::getTitleFor( 'Log' ); + } + + /** + * @param DatabaseBase $db + * @return mixed + */ + public function doQuery( $db ) { + $ids = array_map( 'intval', $this->ids ); + + return $db->select( 'logging', array( + 'log_id', + 'log_type', + 'log_action', + 'log_timestamp', + 'log_user', + 'log_user_text', + 'log_namespace', + 'log_title', + 'log_page', + 'log_comment', + 'log_params', + 'log_deleted' + ), + array( 'log_id' => $ids ), + __METHOD__, + array( 'ORDER BY' => 'log_id DESC' ) + ); + } + + public function newItem( $row ) { + return new RevDelLogItem( $this, $row ); + } + + public function getSuppressBit() { + return Revision::DELETED_RESTRICTED; + } + + public function getLogAction() { + return 'event'; + } + + public function getLogParams( $params ) { + return array( + implode( ',', $params['ids'] ), + "ofield={$params['oldBits']}", + "nfield={$params['newBits']}" + ); + } +} diff --git a/includes/revisiondelete/RevDelRevisionItem.php b/includes/revisiondelete/RevDelRevisionItem.php new file mode 100644 index 00000000..300ce6ad --- /dev/null +++ b/includes/revisiondelete/RevDelRevisionItem.php @@ -0,0 +1,187 @@ +revision = new Revision( $row ); + } + + public function getIdField() { + return 'rev_id'; + } + + public function getTimestampField() { + return 'rev_timestamp'; + } + + public function getAuthorIdField() { + return 'rev_user'; + } + + public function getAuthorNameField() { + return 'rev_user_text'; + } + + public function canView() { + return $this->revision->userCan( Revision::DELETED_RESTRICTED, $this->list->getUser() ); + } + + public function canViewContent() { + return $this->revision->userCan( Revision::DELETED_TEXT, $this->list->getUser() ); + } + + public function getBits() { + return $this->revision->getVisibility(); + } + + public function setBits( $bits ) { + $dbw = wfGetDB( DB_MASTER ); + // Update revision table + $dbw->update( 'revision', + array( 'rev_deleted' => $bits ), + array( + 'rev_id' => $this->revision->getId(), + 'rev_page' => $this->revision->getPage(), + 'rev_deleted' => $this->getBits() + ), + __METHOD__ + ); + if ( !$dbw->affectedRows() ) { + // Concurrent fail! + return false; + } + // Update recentchanges table + $dbw->update( 'recentchanges', + array( + 'rc_deleted' => $bits, + 'rc_patrolled' => 1 + ), + array( + 'rc_this_oldid' => $this->revision->getId(), // condition + // non-unique timestamp index + 'rc_timestamp' => $dbw->timestamp( $this->revision->getTimestamp() ), + ), + __METHOD__ + ); + + return true; + } + + public function isDeleted() { + return $this->revision->isDeleted( Revision::DELETED_TEXT ); + } + + public function isHideCurrentOp( $newBits ) { + return ( $newBits & Revision::DELETED_TEXT ) + && $this->list->getCurrent() == $this->getId(); + } + + /** + * Get the HTML link to the revision text. + * Overridden by RevDelArchiveItem. + * @return string + */ + protected function getRevisionLink() { + $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate( + $this->revision->getTimestamp(), $this->list->getUser() ) ); + + if ( $this->isDeleted() && !$this->canViewContent() ) { + return $date; + } + + return Linker::linkKnown( + $this->list->title, + $date, + array(), + array( + 'oldid' => $this->revision->getId(), + 'unhide' => 1 + ) + ); + } + + /** + * Get the HTML link to the diff. + * Overridden by RevDelArchiveItem + * @return string + */ + protected function getDiffLink() { + if ( $this->isDeleted() && !$this->canViewContent() ) { + return $this->list->msg( 'diff' )->escaped(); + } else { + return Linker::linkKnown( + $this->list->title, + $this->list->msg( 'diff' )->escaped(), + array(), + array( + 'diff' => $this->revision->getId(), + 'oldid' => 'prev', + 'unhide' => 1 + ) + ); + } + } + + public function getHTML() { + $difflink = $this->list->msg( 'parentheses' ) + ->rawParams( $this->getDiffLink() )->escaped(); + $revlink = $this->getRevisionLink(); + $userlink = Linker::revUserLink( $this->revision ); + $comment = Linker::revComment( $this->revision ); + if ( $this->isDeleted() ) { + $revlink = "$revlink"; + } + + return "
  • $difflink $revlink $userlink $comment
  • "; + } + + public function getApiData( ApiResult $result ) { + $rev = $this->revision; + $user = $this->list->getUser(); + $ret = array( + 'id' => $rev->getId(), + 'timestamp' => wfTimestamp( TS_ISO_8601, $rev->getTimestamp() ), + ); + $ret += $rev->isDeleted( Revision::DELETED_USER ) ? array( 'userhidden' => '' ) : array(); + $ret += $rev->isDeleted( Revision::DELETED_COMMENT ) ? array( 'commenthidden' => '' ) : array(); + $ret += $rev->isDeleted( Revision::DELETED_TEXT ) ? array( 'texthidden' => '' ) : array(); + if ( $rev->userCan( Revision::DELETED_USER, $user ) ) { + $ret += array( + 'userid' => $rev->getUser( Revision::FOR_THIS_USER ), + 'user' => $rev->getUserText( Revision::FOR_THIS_USER ), + ); + } + if ( $rev->userCan( Revision::DELETED_COMMENT, $user ) ) { + $ret += array( + 'comment' => $rev->getComment( Revision::FOR_THIS_USER ), + ); + } + + return $ret; + } +} diff --git a/includes/revisiondelete/RevDelRevisionList.php b/includes/revisiondelete/RevDelRevisionList.php new file mode 100644 index 00000000..25450725 --- /dev/null +++ b/includes/revisiondelete/RevDelRevisionList.php @@ -0,0 +1,143 @@ +getTitle() : $target; + } + + /** + * @param DatabaseBase $db + * @return mixed + */ + public function doQuery( $db ) { + $ids = array_map( 'intval', $this->ids ); + $live = $db->select( + array( 'revision', 'page', 'user' ), + array_merge( Revision::selectFields(), Revision::selectUserFields() ), + array( + 'rev_page' => $this->title->getArticleID(), + 'rev_id' => $ids, + ), + __METHOD__, + array( 'ORDER BY' => 'rev_id DESC' ), + array( + 'page' => Revision::pageJoinCond(), + 'user' => Revision::userJoinCond() ) + ); + + if ( $live->numRows() >= count( $ids ) ) { + // All requested revisions are live, keeps things simple! + return $live; + } + + // Check if any requested revisions are available fully deleted. + $archived = $db->select( array( 'archive' ), Revision::selectArchiveFields(), + array( + 'ar_rev_id' => $ids + ), + __METHOD__, + array( 'ORDER BY' => 'ar_rev_id DESC' ) + ); + + if ( $archived->numRows() == 0 ) { + return $live; + } elseif ( $live->numRows() == 0 ) { + return $archived; + } else { + // Combine the two! Whee + $rows = array(); + foreach ( $live as $row ) { + $rows[$row->rev_id] = $row; + } + foreach ( $archived as $row ) { + $rows[$row->ar_rev_id] = $row; + } + krsort( $rows ); + return new FakeResultWrapper( array_values( $rows ) ); + } + } + + public function newItem( $row ) { + if ( isset( $row->rev_id ) ) { + return new RevDelRevisionItem( $this, $row ); + } elseif ( isset( $row->ar_rev_id ) ) { + return new RevDelArchivedRevisionItem( $this, $row ); + } else { + // This shouldn't happen. :) + throw new MWException( 'Invalid row type in RevDelRevisionList' ); + } + } + + public function getCurrent() { + if ( is_null( $this->currentRevId ) ) { + $dbw = wfGetDB( DB_MASTER ); + $this->currentRevId = $dbw->selectField( + 'page', 'page_latest', $this->title->pageCond(), __METHOD__ ); + } + return $this->currentRevId; + } + + public function getSuppressBit() { + return Revision::DELETED_RESTRICTED; + } + + public function doPreCommitUpdates() { + $this->title->invalidateCache(); + return Status::newGood(); + } + + public function doPostCommitUpdates() { + $this->title->purgeSquid(); + // Extensions that require referencing previous revisions may need this + wfRunHooks( 'ArticleRevisionVisibilitySet', array( &$this->title ) ); + return Status::newGood(); + } +} diff --git a/includes/revisiondelete/RevisionDelete.php b/includes/revisiondelete/RevisionDelete.php deleted file mode 100644 index 71850877..00000000 --- a/includes/revisiondelete/RevisionDelete.php +++ /dev/null @@ -1,964 +0,0 @@ -getTitle() : $target; - } - - /** - * @param $db DatabaseBase - * @return mixed - */ - public function doQuery( $db ) { - $ids = array_map( 'intval', $this->ids ); - $live = $db->select( - array( 'revision', 'page', 'user' ), - array_merge( Revision::selectFields(), Revision::selectUserFields() ), - array( - 'rev_page' => $this->title->getArticleID(), - 'rev_id' => $ids, - ), - __METHOD__, - array( 'ORDER BY' => 'rev_id DESC' ), - array( - 'page' => Revision::pageJoinCond(), - 'user' => Revision::userJoinCond() ) - ); - - if ( $live->numRows() >= count( $ids ) ) { - // All requested revisions are live, keeps things simple! - return $live; - } - - // Check if any requested revisions are available fully deleted. - $archived = $db->select( array( 'archive' ), '*', - array( - 'ar_rev_id' => $ids - ), - __METHOD__, - array( 'ORDER BY' => 'ar_rev_id DESC' ) - ); - - if ( $archived->numRows() == 0 ) { - return $live; - } elseif ( $live->numRows() == 0 ) { - return $archived; - } else { - // Combine the two! Whee - $rows = array(); - foreach ( $live as $row ) { - $rows[$row->rev_id] = $row; - } - foreach ( $archived as $row ) { - $rows[$row->ar_rev_id] = $row; - } - krsort( $rows ); - return new FakeResultWrapper( array_values( $rows ) ); - } - } - - public function newItem( $row ) { - if ( isset( $row->rev_id ) ) { - return new RevDel_RevisionItem( $this, $row ); - } elseif ( isset( $row->ar_rev_id ) ) { - return new RevDel_ArchivedRevisionItem( $this, $row ); - } else { - // This shouldn't happen. :) - throw new MWException( 'Invalid row type in RevDel_RevisionList' ); - } - } - - public function getCurrent() { - if ( is_null( $this->currentRevId ) ) { - $dbw = wfGetDB( DB_MASTER ); - $this->currentRevId = $dbw->selectField( - 'page', 'page_latest', $this->title->pageCond(), __METHOD__ ); - } - return $this->currentRevId; - } - - public function getSuppressBit() { - return Revision::DELETED_RESTRICTED; - } - - public function doPreCommitUpdates() { - $this->title->invalidateCache(); - return Status::newGood(); - } - - public function doPostCommitUpdates() { - $this->title->purgeSquid(); - // Extensions that require referencing previous revisions may need this - wfRunHooks( 'ArticleRevisionVisibilitySet', array( &$this->title ) ); - return Status::newGood(); - } -} - -/** - * Item class for a live revision table row - */ -class RevDel_RevisionItem extends RevDel_Item { - var $revision; - - public function __construct( $list, $row ) { - parent::__construct( $list, $row ); - $this->revision = new Revision( $row ); - } - - public function getIdField() { - return 'rev_id'; - } - - public function getTimestampField() { - return 'rev_timestamp'; - } - - public function getAuthorIdField() { - return 'rev_user'; - } - - public function getAuthorNameField() { - return 'user_name'; // see Revision::selectUserFields() - } - - public function canView() { - return $this->revision->userCan( Revision::DELETED_RESTRICTED, $this->list->getUser() ); - } - - public function canViewContent() { - return $this->revision->userCan( Revision::DELETED_TEXT, $this->list->getUser() ); - } - - public function getBits() { - return $this->revision->getVisibility(); - } - - public function setBits( $bits ) { - $dbw = wfGetDB( DB_MASTER ); - // Update revision table - $dbw->update( 'revision', - array( 'rev_deleted' => $bits ), - array( - 'rev_id' => $this->revision->getId(), - 'rev_page' => $this->revision->getPage(), - 'rev_deleted' => $this->getBits() - ), - __METHOD__ - ); - if ( !$dbw->affectedRows() ) { - // Concurrent fail! - return false; - } - // Update recentchanges table - $dbw->update( 'recentchanges', - array( - 'rc_deleted' => $bits, - 'rc_patrolled' => 1 - ), - array( - 'rc_this_oldid' => $this->revision->getId(), // condition - // non-unique timestamp index - 'rc_timestamp' => $dbw->timestamp( $this->revision->getTimestamp() ), - ), - __METHOD__ - ); - return true; - } - - public function isDeleted() { - return $this->revision->isDeleted( Revision::DELETED_TEXT ); - } - - public function isHideCurrentOp( $newBits ) { - return ( $newBits & Revision::DELETED_TEXT ) - && $this->list->getCurrent() == $this->getId(); - } - - /** - * Get the HTML link to the revision text. - * Overridden by RevDel_ArchiveItem. - * @return string - */ - protected function getRevisionLink() { - $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate( - $this->revision->getTimestamp(), $this->list->getUser() ) ); - - if ( $this->isDeleted() && !$this->canViewContent() ) { - return $date; - } - return Linker::linkKnown( - $this->list->title, - $date, - array(), - array( - 'oldid' => $this->revision->getId(), - 'unhide' => 1 - ) - ); - } - - /** - * Get the HTML link to the diff. - * Overridden by RevDel_ArchiveItem - * @return string - */ - protected function getDiffLink() { - if ( $this->isDeleted() && !$this->canViewContent() ) { - return $this->list->msg( 'diff' )->escaped(); - } else { - return Linker::linkKnown( - $this->list->title, - $this->list->msg( 'diff' )->escaped(), - array(), - array( - 'diff' => $this->revision->getId(), - 'oldid' => 'prev', - 'unhide' => 1 - ) - ); - } - } - - public function getHTML() { - $difflink = $this->list->msg( 'parentheses' ) - ->rawParams( $this->getDiffLink() )->escaped(); - $revlink = $this->getRevisionLink(); - $userlink = Linker::revUserLink( $this->revision ); - $comment = Linker::revComment( $this->revision ); - if ( $this->isDeleted() ) { - $revlink = "$revlink"; - } - return "
  • $difflink $revlink $userlink $comment
  • "; - } -} - -/** - * List for archive table items, i.e. revisions deleted via action=delete - */ -class RevDel_ArchiveList extends RevDel_RevisionList { - public function getType() { - return 'archive'; - } - - public static function getRelationType() { - return 'ar_timestamp'; - } - - /** - * @param $db DatabaseBase - * @return mixed - */ - public function doQuery( $db ) { - $timestamps = array(); - foreach ( $this->ids as $id ) { - $timestamps[] = $db->timestamp( $id ); - } - return $db->select( 'archive', '*', - array( - 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDBkey(), - 'ar_timestamp' => $timestamps - ), - __METHOD__, - array( 'ORDER BY' => 'ar_timestamp DESC' ) - ); - } - - public function newItem( $row ) { - return new RevDel_ArchiveItem( $this, $row ); - } - - public function doPreCommitUpdates() { - return Status::newGood(); - } - - public function doPostCommitUpdates() { - return Status::newGood(); - } -} - -/** - * Item class for a archive table row - */ -class RevDel_ArchiveItem extends RevDel_RevisionItem { - public function __construct( $list, $row ) { - RevDel_Item::__construct( $list, $row ); - $this->revision = Revision::newFromArchiveRow( $row, - array( 'page' => $this->list->title->getArticleID() ) ); - } - - public function getIdField() { - return 'ar_timestamp'; - } - - public function getTimestampField() { - return 'ar_timestamp'; - } - - public function getAuthorIdField() { - return 'ar_user'; - } - - public function getAuthorNameField() { - return 'ar_user_text'; - } - - public function getId() { - # Convert DB timestamp to MW timestamp - return $this->revision->getTimestamp(); - } - - public function setBits( $bits ) { - $dbw = wfGetDB( DB_MASTER ); - $dbw->update( 'archive', - array( 'ar_deleted' => $bits ), - array( - 'ar_namespace' => $this->list->title->getNamespace(), - 'ar_title' => $this->list->title->getDBkey(), - // use timestamp for index - 'ar_timestamp' => $this->row->ar_timestamp, - 'ar_rev_id' => $this->row->ar_rev_id, - 'ar_deleted' => $this->getBits() - ), - __METHOD__ ); - return (bool)$dbw->affectedRows(); - } - - protected function getRevisionLink() { - $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate( - $this->revision->getTimestamp(), $this->list->getUser() ) ); - - if ( $this->isDeleted() && !$this->canViewContent() ) { - return $date; - } - - return Linker::link( - SpecialPage::getTitleFor( 'Undelete' ), - $date, - array(), - array( - 'target' => $this->list->title->getPrefixedText(), - 'timestamp' => $this->revision->getTimestamp() - ) - ); - } - - protected function getDiffLink() { - if ( $this->isDeleted() && !$this->canViewContent() ) { - return $this->list->msg( 'diff' )->escaped(); - } - - return Linker::link( - SpecialPage::getTitleFor( 'Undelete' ), - $this->list->msg( 'diff' )->escaped(), - array(), - array( - 'target' => $this->list->title->getPrefixedText(), - 'diff' => 'prev', - 'timestamp' => $this->revision->getTimestamp() - ) - ); - } -} - -/** - * Item class for a archive table row by ar_rev_id -- actually - * used via RevDel_RevisionList. - */ -class RevDel_ArchivedRevisionItem extends RevDel_ArchiveItem { - public function __construct( $list, $row ) { - RevDel_Item::__construct( $list, $row ); - - $this->revision = Revision::newFromArchiveRow( $row, - array( 'page' => $this->list->title->getArticleID() ) ); - } - - public function getIdField() { - return 'ar_rev_id'; - } - - public function getId() { - return $this->revision->getId(); - } - - public function setBits( $bits ) { - $dbw = wfGetDB( DB_MASTER ); - $dbw->update( 'archive', - array( 'ar_deleted' => $bits ), - array( 'ar_rev_id' => $this->row->ar_rev_id, - 'ar_deleted' => $this->getBits() - ), - __METHOD__ ); - return (bool)$dbw->affectedRows(); - } -} - -/** - * List for oldimage table items - */ -class RevDel_FileList extends RevDel_List { - public function getType() { - return 'oldimage'; - } - - public static function getRelationType() { - return 'oi_archive_name'; - } - - public static function getRestriction() { - return 'deleterevision'; - } - - public static function getRevdelConstant() { - return File::DELETED_FILE; - } - - var $storeBatch, $deleteBatch, $cleanupBatch; - - /** - * @param $db DatabaseBase - * @return mixed - */ - public function doQuery( $db ) { - $archiveNames = array(); - foreach ( $this->ids as $timestamp ) { - $archiveNames[] = $timestamp . '!' . $this->title->getDBkey(); - } - return $db->select( - 'oldimage', - OldLocalFile::selectFields(), - array( - 'oi_name' => $this->title->getDBkey(), - 'oi_archive_name' => $archiveNames - ), - __METHOD__, - array( 'ORDER BY' => 'oi_timestamp DESC' ) - ); - } - - public function newItem( $row ) { - return new RevDel_FileItem( $this, $row ); - } - - public function clearFileOps() { - $this->deleteBatch = array(); - $this->storeBatch = array(); - $this->cleanupBatch = array(); - } - - public function doPreCommitUpdates() { - $status = Status::newGood(); - $repo = RepoGroup::singleton()->getLocalRepo(); - if ( $this->storeBatch ) { - $status->merge( $repo->storeBatch( $this->storeBatch, FileRepo::OVERWRITE_SAME ) ); - } - if ( !$status->isOK() ) { - return $status; - } - if ( $this->deleteBatch ) { - $status->merge( $repo->deleteBatch( $this->deleteBatch ) ); - } - if ( !$status->isOK() ) { - // Running cleanupDeletedBatch() after a failed storeBatch() with the DB already - // modified (but destined for rollback) causes data loss - return $status; - } - if ( $this->cleanupBatch ) { - $status->merge( $repo->cleanupDeletedBatch( $this->cleanupBatch ) ); - } - return $status; - } - - public function doPostCommitUpdates() { - global $wgUseSquid; - $file = wfLocalFile( $this->title ); - $file->purgeCache(); - $file->purgeDescription(); - $purgeUrls = array(); - foreach ( $this->ids as $timestamp ) { - $archiveName = $timestamp . '!' . $this->title->getDBkey(); - $file->purgeOldThumbnails( $archiveName ); - $purgeUrls[] = $file->getArchiveUrl( $archiveName ); - } - if ( $wgUseSquid ) { - // purge full images from cache - SquidUpdate::purge( $purgeUrls ); - } - return Status::newGood(); - } - - public function getSuppressBit() { - return File::DELETED_RESTRICTED; - } -} - -/** - * Item class for an oldimage table row - */ -class RevDel_FileItem extends RevDel_Item { - - /** - * @var File - */ - var $file; - - public function __construct( $list, $row ) { - parent::__construct( $list, $row ); - $this->file = RepoGroup::singleton()->getLocalRepo()->newFileFromRow( $row ); - } - - public function getIdField() { - return 'oi_archive_name'; - } - - public function getTimestampField() { - return 'oi_timestamp'; - } - - public function getAuthorIdField() { - return 'oi_user'; - } - - public function getAuthorNameField() { - return 'oi_user_text'; - } - - public function getId() { - $parts = explode( '!', $this->row->oi_archive_name ); - return $parts[0]; - } - - public function canView() { - return $this->file->userCan( File::DELETED_RESTRICTED, $this->list->getUser() ); - } - - public function canViewContent() { - return $this->file->userCan( File::DELETED_FILE, $this->list->getUser() ); - } - - public function getBits() { - return $this->file->getVisibility(); - } - - public function setBits( $bits ) { - # Queue the file op - # @todo FIXME: Move to LocalFile.php - if ( $this->isDeleted() ) { - if ( $bits & File::DELETED_FILE ) { - # Still deleted - } else { - # Newly undeleted - $key = $this->file->getStorageKey(); - $srcRel = $this->file->repo->getDeletedHashPath( $key ) . $key; - $this->list->storeBatch[] = array( - $this->file->repo->getVirtualUrl( 'deleted' ) . '/' . $srcRel, - 'public', - $this->file->getRel() - ); - $this->list->cleanupBatch[] = $key; - } - } elseif ( $bits & File::DELETED_FILE ) { - # Newly deleted - $key = $this->file->getStorageKey(); - $dstRel = $this->file->repo->getDeletedHashPath( $key ) . $key; - $this->list->deleteBatch[] = array( $this->file->getRel(), $dstRel ); - } - - # Do the database operations - $dbw = wfGetDB( DB_MASTER ); - $dbw->update( 'oldimage', - array( 'oi_deleted' => $bits ), - array( - 'oi_name' => $this->row->oi_name, - 'oi_timestamp' => $this->row->oi_timestamp, - 'oi_deleted' => $this->getBits() - ), - __METHOD__ - ); - return (bool)$dbw->affectedRows(); - } - - public function isDeleted() { - return $this->file->isDeleted( File::DELETED_FILE ); - } - - /** - * Get the link to the file. - * Overridden by RevDel_ArchivedFileItem. - * @return string - */ - protected function getLink() { - $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate( - $this->file->getTimestamp(), $this->list->getUser() ) ); - - if ( !$this->isDeleted() ) { - # Regular files... - return Html::rawElement( 'a', array( 'href' => $this->file->getUrl() ), $date ); - } - - # Hidden files... - if ( !$this->canViewContent() ) { - $link = $date; - } else { - $link = Linker::link( - SpecialPage::getTitleFor( 'Revisiondelete' ), - $date, - array(), - array( - 'target' => $this->list->title->getPrefixedText(), - 'file' => $this->file->getArchiveName(), - 'token' => $this->list->getUser()->getEditToken( - $this->file->getArchiveName() ) - ) - ); - } - return '' . $link . ''; - } - /** - * Generate a user tool link cluster if the current user is allowed to view it - * @return string HTML - */ - protected function getUserTools() { - if ( $this->file->userCan( Revision::DELETED_USER, $this->list->getUser() ) ) { - $link = Linker::userLink( $this->file->user, $this->file->user_text ) . - Linker::userToolLinks( $this->file->user, $this->file->user_text ); - } else { - $link = $this->list->msg( 'rev-deleted-user' )->escaped(); - } - if ( $this->file->isDeleted( Revision::DELETED_USER ) ) { - return '' . $link . ''; - } - return $link; - } - - /** - * Wrap and format the file's comment block, if the current - * user is allowed to view it. - * - * @return string HTML - */ - protected function getComment() { - if ( $this->file->userCan( File::DELETED_COMMENT, $this->list->getUser() ) ) { - $block = Linker::commentBlock( $this->file->description ); - } else { - $block = ' ' . $this->list->msg( 'rev-deleted-comment' )->escaped(); - } - if ( $this->file->isDeleted( File::DELETED_COMMENT ) ) { - return "$block"; - } - return $block; - } - - public function getHTML() { - $data = - $this->list->msg( 'widthheight' )->numParams( - $this->file->getWidth(), $this->file->getHeight() )->text() . - ' (' . $this->list->msg( 'nbytes' )->numParams( $this->file->getSize() )->text() . ')'; - - return '
  • ' . $this->getLink() . ' ' . $this->getUserTools() . ' ' . - $data . ' ' . $this->getComment() . '
  • '; - } -} - -/** - * List for filearchive table items - */ -class RevDel_ArchivedFileList extends RevDel_FileList { - public function getType() { - return 'filearchive'; - } - - public static function getRelationType() { - return 'fa_id'; - } - - /** - * @param $db DatabaseBase - * @return mixed - */ - public function doQuery( $db ) { - $ids = array_map( 'intval', $this->ids ); - return $db->select( - 'filearchive', - ArchivedFile::selectFields(), - array( - 'fa_name' => $this->title->getDBkey(), - 'fa_id' => $ids - ), - __METHOD__, - array( 'ORDER BY' => 'fa_id DESC' ) - ); - } - - public function newItem( $row ) { - return new RevDel_ArchivedFileItem( $this, $row ); - } -} - -/** - * Item class for a filearchive table row - */ -class RevDel_ArchivedFileItem extends RevDel_FileItem { - public function __construct( $list, $row ) { - RevDel_Item::__construct( $list, $row ); - $this->file = ArchivedFile::newFromRow( $row ); - } - - public function getIdField() { - return 'fa_id'; - } - - public function getTimestampField() { - return 'fa_timestamp'; - } - - public function getAuthorIdField() { - return 'fa_user'; - } - - public function getAuthorNameField() { - return 'fa_user_text'; - } - - public function getId() { - return $this->row->fa_id; - } - - public function setBits( $bits ) { - $dbw = wfGetDB( DB_MASTER ); - $dbw->update( 'filearchive', - array( 'fa_deleted' => $bits ), - array( - 'fa_id' => $this->row->fa_id, - 'fa_deleted' => $this->getBits(), - ), - __METHOD__ - ); - return (bool)$dbw->affectedRows(); - } - - protected function getLink() { - $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate( - $this->file->getTimestamp(), $this->list->getUser() ) ); - - # Hidden files... - if ( !$this->canViewContent() ) { - $link = $date; - } else { - $undelete = SpecialPage::getTitleFor( 'Undelete' ); - $key = $this->file->getKey(); - $link = Linker::link( $undelete, $date, array(), - array( - 'target' => $this->list->title->getPrefixedText(), - 'file' => $key, - 'token' => $this->list->getUser()->getEditToken( $key ) - ) - ); - } - if ( $this->isDeleted() ) { - $link = '' . $link . ''; - } - return $link; - } -} - -/** - * List for logging table items - */ -class RevDel_LogList extends RevDel_List { - public function getType() { - return 'logging'; - } - - public static function getRelationType() { - return 'log_id'; - } - - public static function getRestriction() { - return 'deletelogentry'; - } - - public static function getRevdelConstant() { - return LogPage::DELETED_ACTION; - } - - public static function suggestTarget( $target, array $ids ) { - $result = wfGetDB( DB_SLAVE )->select( 'logging', - 'log_type', - array( 'log_id' => $ids ), - __METHOD__, - array( 'DISTINCT' ) - ); - if ( $result->numRows() == 1 ) { - // If there's only one type, the target can be set to include it. - return SpecialPage::getTitleFor( 'Log', $result->current()->log_type ); - } - return SpecialPage::getTitleFor( 'Log' ); - } - - /** - * @param $db DatabaseBase - * @return mixed - */ - public function doQuery( $db ) { - $ids = array_map( 'intval', $this->ids ); - return $db->select( 'logging', '*', - array( 'log_id' => $ids ), - __METHOD__, - array( 'ORDER BY' => 'log_id DESC' ) - ); - } - - public function newItem( $row ) { - return new RevDel_LogItem( $this, $row ); - } - - public function getSuppressBit() { - return Revision::DELETED_RESTRICTED; - } - - public function getLogAction() { - return 'event'; - } - - public function getLogParams( $params ) { - return array( - implode( ',', $params['ids'] ), - "ofield={$params['oldBits']}", - "nfield={$params['newBits']}" - ); - } -} - -/** - * Item class for a logging table row - */ -class RevDel_LogItem extends RevDel_Item { - public function getIdField() { - return 'log_id'; - } - - public function getTimestampField() { - return 'log_timestamp'; - } - - public function getAuthorIdField() { - return 'log_user'; - } - - public function getAuthorNameField() { - return 'log_user_text'; - } - - public function canView() { - return LogEventsList::userCan( $this->row, Revision::DELETED_RESTRICTED, $this->list->getUser() ); - } - - public function canViewContent() { - return true; // none - } - - public function getBits() { - return $this->row->log_deleted; - } - - public function setBits( $bits ) { - $dbw = wfGetDB( DB_MASTER ); - $dbw->update( 'recentchanges', - array( - 'rc_deleted' => $bits, - 'rc_patrolled' => 1 - ), - array( - 'rc_logid' => $this->row->log_id, - 'rc_timestamp' => $this->row->log_timestamp // index - ), - __METHOD__ - ); - $dbw->update( 'logging', - array( 'log_deleted' => $bits ), - array( - 'log_id' => $this->row->log_id, - 'log_deleted' => $this->getBits() - ), - __METHOD__ - ); - return (bool)$dbw->affectedRows(); - } - - public function getHTML() { - $date = htmlspecialchars( $this->list->getLanguage()->userTimeAndDate( - $this->row->log_timestamp, $this->list->getUser() ) ); - $title = Title::makeTitle( $this->row->log_namespace, $this->row->log_title ); - $formatter = LogFormatter::newFromRow( $this->row ); - $formatter->setContext( $this->list->getContext() ); - $formatter->setAudience( LogFormatter::FOR_THIS_USER ); - - // Log link for this page - $loglink = Linker::link( - SpecialPage::getTitleFor( 'Log' ), - $this->list->msg( 'log' )->escaped(), - array(), - array( 'page' => $title->getPrefixedText() ) - ); - $loglink = $this->list->msg( 'parentheses' )->rawParams( $loglink )->escaped(); - // User links and action text - $action = $formatter->getActionText(); - // Comment - $comment = $this->list->getLanguage()->getDirMark() . Linker::commentBlock( $this->row->log_comment ); - if ( LogEventsList::isDeleted( $this->row, LogPage::DELETED_COMMENT ) ) { - $comment = '' . $comment . ''; - } - - return "
  • $loglink $date $action $comment
  • "; - } -} diff --git a/includes/revisiondelete/RevisionDeleteAbstracts.php b/includes/revisiondelete/RevisionDeleteAbstracts.php deleted file mode 100644 index 803467e6..00000000 --- a/includes/revisiondelete/RevisionDeleteAbstracts.php +++ /dev/null @@ -1,328 +0,0 @@ -ids = $ids; - } - - /** - * Get the DB field name associated with the ID list. - * This used to populate the log_search table for finding log entries. - * Override this function. - * @return string|null - */ - public static function getRelationType() { - return null; - } - - /** - * Get the user right required for this list type - * Override this function. - * @since 1.22 - * @return string|null - */ - public static function getRestriction() { - return null; - } - - /** - * Get the revision deletion constant for this list type - * Override this function. - * @since 1.22 - * @return int|null - */ - public static function getRevdelConstant() { - return null; - } - - /** - * Suggest a target for the revision deletion - * Optionally override this function. - * @since 1.22 - * @param Title|null $target User-supplied target - * @param array $ids - * @return Title|null - */ - public static function suggestTarget( $target, array $ids ) { - return $target; - } - - /** - * Set the visibility for the revisions in this list. Logging and - * transactions are done here. - * - * @param array $params Associative array of parameters. Members are: - * value: The integer value to set the visibility to - * comment: The log comment. - * @return Status - */ - public function setVisibility( $params ) { - $bitPars = $params['value']; - $comment = $params['comment']; - - $this->res = false; - $dbw = wfGetDB( DB_MASTER ); - $this->doQuery( $dbw ); - $dbw->begin( __METHOD__ ); - $status = Status::newGood(); - $missing = array_flip( $this->ids ); - $this->clearFileOps(); - $idsForLog = array(); - $authorIds = $authorIPs = array(); - - for ( $this->reset(); $this->current(); $this->next() ) { - $item = $this->current(); - unset( $missing[$item->getId()] ); - - $oldBits = $item->getBits(); - // Build the actual new rev_deleted bitfield - $newBits = RevisionDeleter::extractBitfield( $bitPars, $oldBits ); - - if ( $oldBits == $newBits ) { - $status->warning( 'revdelete-no-change', $item->formatDate(), $item->formatTime() ); - $status->failCount++; - continue; - } elseif ( $oldBits == 0 && $newBits != 0 ) { - $opType = 'hide'; - } elseif ( $oldBits != 0 && $newBits == 0 ) { - $opType = 'show'; - } else { - $opType = 'modify'; - } - - if ( $item->isHideCurrentOp( $newBits ) ) { - // Cannot hide current version text - $status->error( 'revdelete-hide-current', $item->formatDate(), $item->formatTime() ); - $status->failCount++; - continue; - } - if ( !$item->canView() ) { - // Cannot access this revision - $msg = ( $opType == 'show' ) ? - 'revdelete-show-no-access' : 'revdelete-modify-no-access'; - $status->error( $msg, $item->formatDate(), $item->formatTime() ); - $status->failCount++; - continue; - } - // Cannot just "hide from Sysops" without hiding any fields - if ( $newBits == Revision::DELETED_RESTRICTED ) { - $status->warning( 'revdelete-only-restricted', $item->formatDate(), $item->formatTime() ); - $status->failCount++; - continue; - } - - // Update the revision - $ok = $item->setBits( $newBits ); - - if ( $ok ) { - $idsForLog[] = $item->getId(); - $status->successCount++; - if ( $item->getAuthorId() > 0 ) { - $authorIds[] = $item->getAuthorId(); - } elseif ( IP::isIPAddress( $item->getAuthorName() ) ) { - $authorIPs[] = $item->getAuthorName(); - } - } else { - $status->error( 'revdelete-concurrent-change', $item->formatDate(), $item->formatTime() ); - $status->failCount++; - } - } - - // Handle missing revisions - foreach ( $missing as $id => $unused ) { - $status->error( 'revdelete-modify-missing', $id ); - $status->failCount++; - } - - if ( $status->successCount == 0 ) { - $status->ok = false; - $dbw->rollback( __METHOD__ ); - return $status; - } - - // Save success count - $successCount = $status->successCount; - - // Move files, if there are any - $status->merge( $this->doPreCommitUpdates() ); - if ( !$status->isOK() ) { - // Fatal error, such as no configured archive directory - $dbw->rollback( __METHOD__ ); - return $status; - } - - // Log it - $this->updateLog( array( - 'title' => $this->title, - 'count' => $successCount, - 'newBits' => $newBits, - 'oldBits' => $oldBits, - 'comment' => $comment, - 'ids' => $idsForLog, - 'authorIds' => $authorIds, - 'authorIPs' => $authorIPs - ) ); - $dbw->commit( __METHOD__ ); - - // Clear caches - $status->merge( $this->doPostCommitUpdates() ); - return $status; - } - - /** - * Reload the list data from the master DB. This can be done after setVisibility() - * to allow $item->getHTML() to show the new data. - */ - function reloadFromMaster() { - $dbw = wfGetDB( DB_MASTER ); - $this->res = $this->doQuery( $dbw ); - } - - /** - * Record a log entry on the action - * @param array $params Associative array of parameters: - * newBits: The new value of the *_deleted bitfield - * oldBits: The old value of the *_deleted bitfield. - * title: The target title - * ids: The ID list - * comment: The log comment - * authorsIds: The array of the user IDs of the offenders - * authorsIPs: The array of the IP/anon user offenders - * @throws MWException - */ - protected function updateLog( $params ) { - // Get the URL param's corresponding DB field - $field = RevisionDeleter::getRelationType( $this->getType() ); - if ( !$field ) { - throw new MWException( "Bad log URL param type!" ); - } - // Put things hidden from sysops in the oversight log - if ( ( $params['newBits'] | $params['oldBits'] ) & $this->getSuppressBit() ) { - $logType = 'suppress'; - } else { - $logType = 'delete'; - } - // Add params for effected page and ids - $logParams = $this->getLogParams( $params ); - // Actually add the deletion log entry - $log = new LogPage( $logType ); - $logid = $log->addEntry( $this->getLogAction(), $params['title'], - $params['comment'], $logParams, $this->getUser() ); - // Allow for easy searching of deletion log items for revision/log items - $log->addRelations( $field, $params['ids'], $logid ); - $log->addRelations( 'target_author_id', $params['authorIds'], $logid ); - $log->addRelations( 'target_author_ip', $params['authorIPs'], $logid ); - } - - /** - * Get the log action for this list type - * @return string - */ - public function getLogAction() { - return 'revision'; - } - - /** - * Get log parameter array. - * @param array $params Associative array of log parameters, same as updateLog() - * @return array - */ - public function getLogParams( $params ) { - return array( - $this->getType(), - implode( ',', $params['ids'] ), - "ofield={$params['oldBits']}", - "nfield={$params['newBits']}" - ); - } - - /** - * Clear any data structures needed for doPreCommitUpdates() and doPostCommitUpdates() - * STUB - */ - public function clearFileOps() { - } - - /** - * A hook for setVisibility(): do batch updates pre-commit. - * STUB - * @return Status - */ - public function doPreCommitUpdates() { - return Status::newGood(); - } - - /** - * A hook for setVisibility(): do any necessary updates post-commit. - * STUB - * @return Status - */ - public function doPostCommitUpdates() { - return Status::newGood(); - } - - /** - * Get the integer value of the flag used for suppression - */ - abstract public function getSuppressBit(); -} - -/** - * Abstract base class for deletable items - */ -abstract class RevDel_Item extends RevisionItemBase { - /** - * Returns true if the item is "current", and the operation to set the given - * bits can't be executed for that reason - * STUB - * @return bool - */ - public function isHideCurrentOp( $newBits ) { - return false; - } - - /** - * Get the current deletion bitfield value - */ - abstract public function getBits(); - - /** - * Set the visibility of the item. This should do any necessary DB queries. - * - * The DB update query should have a condition which forces it to only update - * if the value in the DB matches the value fetched earlier with the SELECT. - * If the update fails because it did not match, the function should return - * false. This prevents concurrency problems. - * - * @return boolean success - */ - abstract public function setBits( $newBits ); -} diff --git a/includes/revisiondelete/RevisionDeleteUser.php b/includes/revisiondelete/RevisionDeleteUser.php index 4505ee10..55c46c5e 100644 --- a/includes/revisiondelete/RevisionDeleteUser.php +++ b/includes/revisiondelete/RevisionDeleteUser.php @@ -33,10 +33,10 @@ class RevisionDeleteUser { /** * Update *_deleted bitfields in various tables to hide or unhide usernames - * @param $name String username - * @param $userId Int user id - * @param $op String operator '|' or '&' - * @param $dbw null|DatabaseBase, if you happen to have one lying around + * @param string $name Username + * @param int $userId User id + * @param string $op Operator '|' or '&' + * @param null|DatabaseBase $dbw If you happen to have one lying around * @return bool */ private static function setUsernameBitfields( $name, $userId, $op, $dbw ) { @@ -55,8 +55,8 @@ class RevisionDeleteUser { $delUser = Revision::DELETED_USER | Revision::DELETED_RESTRICTED; $delAction = LogPage::DELETED_ACTION | Revision::DELETED_RESTRICTED; if ( $op == '&' ) { - $delUser = "~{$delUser}"; - $delAction = "~{$delAction}"; + $delUser = $dbw->bitNot( $delUser ); + $delAction = $dbw->bitNot( $delAction ); } # Normalize user name @@ -66,14 +66,14 @@ class RevisionDeleteUser { # Hide name from live edits $dbw->update( 'revision', - array( "rev_deleted = rev_deleted $op $delUser" ), + array( self::buildSetBitDeletedField( 'rev_deleted', $op, $delUser, $dbw ) ), array( 'rev_user' => $userId ), __METHOD__ ); # Hide name from deleted edits $dbw->update( 'archive', - array( "ar_deleted = ar_deleted $op $delUser" ), + array( self::buildSetBitDeletedField( 'ar_deleted', $op, $delUser, $dbw ) ), array( 'ar_user_text' => $name ), __METHOD__ ); @@ -81,28 +81,28 @@ class RevisionDeleteUser { # Hide name from logs $dbw->update( 'logging', - array( "log_deleted = log_deleted $op $delUser" ), - array( 'log_user' => $userId, "log_type != 'suppress'" ), + array( self::buildSetBitDeletedField( 'log_deleted', $op, $delUser, $dbw ) ), + array( 'log_user' => $userId, 'log_type != ' . $dbw->addQuotes( 'suppress' ) ), __METHOD__ ); $dbw->update( 'logging', - array( "log_deleted = log_deleted $op $delAction" ), + array( self::buildSetBitDeletedField( 'log_deleted', $op, $delAction, $dbw ) ), array( 'log_namespace' => NS_USER, 'log_title' => $userDbKey, - "log_type != 'suppress'" ), + 'log_type != ' . $dbw->addQuotes( 'suppress' ) ), __METHOD__ ); # Hide name from RC $dbw->update( 'recentchanges', - array( "rc_deleted = rc_deleted $op $delUser" ), + array( self::buildSetBitDeletedField( 'rc_deleted', $op, $delUser, $dbw ) ), array( 'rc_user_text' => $name ), __METHOD__ ); $dbw->update( 'recentchanges', - array( "rc_deleted = rc_deleted $op $delAction" ), + array( self::buildSetBitDeletedField( 'rc_deleted', $op, $delAction, $dbw ) ), array( 'rc_namespace' => NS_USER, 'rc_title' => $userDbKey, 'rc_logid > 0' ), __METHOD__ ); @@ -110,7 +110,7 @@ class RevisionDeleteUser { # Hide name from live images $dbw->update( 'oldimage', - array( "oi_deleted = oi_deleted $op $delUser" ), + array( self::buildSetBitDeletedField( 'oi_deleted', $op, $delUser, $dbw ) ), array( 'oi_user_text' => $name ), __METHOD__ ); @@ -118,7 +118,7 @@ class RevisionDeleteUser { # Hide name from deleted images $dbw->update( 'filearchive', - array( "fa_deleted = fa_deleted $op $delUser" ), + array( self::buildSetBitDeletedField( 'fa_deleted', $op, $delUser, $dbw ) ), array( 'fa_user_text' => $name ), __METHOD__ ); @@ -126,6 +126,12 @@ class RevisionDeleteUser { return true; } + private static function buildSetBitDeletedField( $field, $op, $value, $dbw ) { + return $field . ' = ' . ( $op === '&' + ? $dbw->bitAnd( $field, $value ) + : $dbw->bitOr( $field, $value ) ); + } + public static function suppressUserName( $name, $userId, $dbw = null ) { return self::setUsernameBitfields( $name, $userId, '|', $dbw ); } diff --git a/includes/revisiondelete/RevisionDeleter.php b/includes/revisiondelete/RevisionDeleter.php index dbcb3d7d..d4f81678 100644 --- a/includes/revisiondelete/RevisionDeleter.php +++ b/includes/revisiondelete/RevisionDeleter.php @@ -29,11 +29,11 @@ class RevisionDeleter { /** List of known revdel types, with their corresponding list classes */ private static $allowedTypes = array( - 'revision' => 'RevDel_RevisionList', - 'archive' => 'RevDel_ArchiveList', - 'oldimage' => 'RevDel_FileList', - 'filearchive' => 'RevDel_ArchivedFileList', - 'logging' => 'RevDel_LogList', + 'revision' => 'RevDelRevisionList', + 'archive' => 'RevDelArchiveList', + 'oldimage' => 'RevDelFileList', + 'filearchive' => 'RevDelArchivedFileList', + 'logging' => 'RevDelLogList', ); /** Type map to support old log entries */ @@ -77,7 +77,7 @@ class RevisionDeleter { * @param IContextSource $context * @param Title $title * @param array $ids - * @return RevDel_List + * @return RevDelList */ public static function createList( $typeName, IContextSource $context, Title $title, array $ids ) { $typeName = self::getCanonicalTypeName( $typeName ); @@ -91,12 +91,12 @@ class RevisionDeleter { * Checks for a change in the bitfield for a certain option and updates the * provided array accordingly. * - * @param string $desc description to add to the array if the option was + * @param string $desc Description to add to the array if the option was * enabled / disabled. - * @param $field Integer: the bitmask describing the single option. - * @param $diff Integer: the xor of the old and new bitfields. - * @param $new Integer: the new bitfield - * @param array $arr the array to update. + * @param int $field The bitmask describing the single option. + * @param int $diff The xor of the old and new bitfields. + * @param int $new The new bitfield + * @param array $arr The array to update. */ protected static function checkItem( $desc, $field, $diff, $new, &$arr ) { if ( $diff & $field ) { @@ -117,8 +117,8 @@ class RevisionDeleter { * You can turn the keys in $arr[0] and $arr[1] into message keys by * appending -hid and and -unhid to the keys respectively. * - * @param $n Integer: the new bitfield. - * @param $o Integer: the old bitfield. + * @param int $n The new bitfield. + * @param int $o The old bitfield. * @return array An array as described above. * @since 1.19 public */ @@ -189,7 +189,7 @@ class RevisionDeleter { * Suggest a target for the revision deletion * @since 1.22 * @param string $typeName - * @param Title|null $title User-supplied target + * @param Title|null $target User-supplied target * @param array $ids * @return Title|null */ @@ -201,14 +201,13 @@ class RevisionDeleter { return call_user_func( array( self::$allowedTypes[$typeName], 'suggestTarget' ), $target, $ids ); } - /** * Checks if a revision still exists in the revision table. * If it doesn't, returns the corresponding ar_timestamp field * so that this key can be used instead. * - * @param $title Title - * @param $revid + * @param Title $title + * @param int $revid * @return bool|mixed */ public static function checkRevisionExistence( $title, $revid ) { @@ -231,8 +230,8 @@ class RevisionDeleter { /** * Put together a rev_deleted bitfield * @since 1.22 - * @param array $bitPars extractBitParams() params - * @param int $oldfield current bitfield + * @param array $bitPars ExtractBitParams() params + * @param int $oldfield Current bitfield * @return array */ public static function extractBitfield( $bitPars, $oldfield ) { -- cgit v1.2.2