Файловый менеджер - Редактировать - /var/www/html/revisiondelete.zip
Ðазад
PK ! �A�� � RevDelArchivedFileItem.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\Api\ApiResult; use MediaWiki\MediaWikiServices; use MediaWiki\RevisionList\RevisionListBase; use MediaWiki\SpecialPage\SpecialPage; /** * Item class for a filearchive table row * * @property ArchivedFile $file * @property RevDelArchivedFileList $list */ class RevDelArchivedFileItem extends RevDelFileItem { /** @var LocalFile */ protected $lockFile; public function __construct( RevisionListBase $list, $row ) { parent::__construct( $list, $row ); $this->lockFile = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo() ->newFile( $row->fa_name ); } protected static function initFile( $list, $row ) { return 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 getAuthorActorField() { return 'fa_actor'; } public function getId() { return $this->row->fa_id; } public function setBits( $bits ) { $dbw = $this->dbProvider->getPrimaryDatabase(); $dbw->newUpdateQueryBuilder() ->update( 'filearchive' ) ->set( [ 'fa_deleted' => $bits ] ) ->where( [ 'fa_id' => $this->row->fa_id, 'fa_deleted' => $this->getBits(), ] ) ->caller( __METHOD__ )->execute(); return (bool)$dbw->affectedRows(); } protected function getLink() { $date = $this->list->getLanguage()->userTimeAndDate( $this->file->getTimestamp(), $this->list->getUser() ); # Hidden files... if ( !$this->canViewContent() ) { $link = htmlspecialchars( $date ); } else { $undelete = SpecialPage::getTitleFor( 'Undelete' ); $key = $this->file->getKey(); $link = $this->getLinkRenderer()->makeLink( $undelete, $date, [], [ 'target' => $this->list->getPageName(), 'file' => $key, 'token' => $this->list->getUser()->getEditToken( $key ) ] ); } if ( $this->isDeleted() ) { $link = '<span class="history-deleted">' . $link . '</span>'; } return $link; } public function getApiData( ApiResult $result ) { $file = $this->file; $user = $this->list->getUser(); $ret = [ 'title' => $this->list->getPageName(), 'timestamp' => wfTimestamp( TS_ISO_8601, $file->getTimestamp() ), 'width' => $file->getWidth(), 'height' => $file->getHeight(), 'size' => $file->getSize(), 'userhidden' => (bool)$file->isDeleted( File::DELETED_USER ), 'commenthidden' => (bool)$file->isDeleted( File::DELETED_COMMENT ), 'contenthidden' => (bool)$this->isDeleted(), ]; if ( $this->canViewContent() ) { $ret += [ 'url' => SpecialPage::getTitleFor( 'Revisiondelete' )->getLinkURL( [ 'target' => $this->list->getPageName(), 'file' => $file->getKey(), 'token' => $user->getEditToken( $file->getKey() ) ] ), ]; } $uploader = $file->getUploader( ArchivedFile::FOR_THIS_USER, $user ); if ( $uploader ) { $ret += [ 'userid' => $uploader->getId(), 'user' => $uploader->getName(), ]; } $comment = $file->getDescription( ArchivedFile::FOR_THIS_USER, $user ); if ( $comment !== '' ) { $ret += [ 'comment' => $comment, ]; } return $ret; } public function lock() { return $this->lockFile->acquireFileLock(); } public function unlock() { return $this->lockFile->releaseFileLock(); } } PK ! 1_��f f RevDelFileItem.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\Api\ApiResult; use MediaWiki\Html\Html; use MediaWiki\Linker\Linker; use MediaWiki\MediaWikiServices; use MediaWiki\RevisionList\RevisionListBase; use MediaWiki\SpecialPage\SpecialPage; use Wikimedia\Rdbms\IConnectionProvider; /** * Item class for an oldimage table row */ class RevDelFileItem extends RevDelItem { /** @var RevDelFileList */ protected $list; /** @var OldLocalFile */ protected $file; protected IConnectionProvider $dbProvider; public function __construct( RevisionListBase $list, $row ) { parent::__construct( $list, $row ); $this->file = static::initFile( $list, $row ); $this->dbProvider = MediaWikiServices::getInstance()->getConnectionProvider(); } /** * Create file object from $row sourced from $list * * @param RevisionListBase $list * @param mixed $row * @return mixed */ protected static function initFile( $list, $row ) { return MediaWikiServices::getInstance()->getRepoGroup()->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 getAuthorActorField() { return 'oi_actor'; } 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->getAuthority() ); } public function canViewContent() { return $this->file->userCan( File::DELETED_FILE, $this->list->getAuthority() ); } 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[] = [ $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[] = [ $this->file->getRel(), $dstRel ]; } # Do the database operations $dbw = $this->dbProvider->getPrimaryDatabase(); $dbw->newUpdateQueryBuilder() ->update( 'oldimage' ) ->set( [ 'oi_deleted' => $bits ] ) ->where( [ 'oi_name' => $this->row->oi_name, 'oi_timestamp' => $this->row->oi_timestamp, 'oi_deleted' => $this->getBits() ] ) ->caller( __METHOD__ )->execute(); 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 = $this->list->getLanguage()->userTimeAndDate( $this->file->getTimestamp(), $this->list->getUser() ); if ( !$this->isDeleted() ) { # Regular files... return Html::element( 'a', [ 'href' => $this->file->getUrl() ], $date ); } # Hidden files... if ( !$this->canViewContent() ) { $link = htmlspecialchars( $date ); } else { $link = $this->getLinkRenderer()->makeLink( SpecialPage::getTitleFor( 'Revisiondelete' ), $date, [], [ 'target' => $this->list->getPageName(), 'file' => $this->file->getArchiveName(), 'token' => $this->list->getUser()->getEditToken( $this->file->getArchiveName() ) ] ); } return '<span class="history-deleted">' . $link . '</span>'; } /** * Generate a user tool link cluster if the current user is allowed to view it * @return string HTML */ protected function getUserTools() { $uploader = $this->file->getUploader( File::FOR_THIS_USER, $this->list->getAuthority() ); if ( $uploader ) { $link = Linker::userLink( $uploader->getId(), $uploader->getName() ) . Linker::userToolLinks( $uploader->getId(), $uploader->getName() ); return $link; } else { $link = $this->list->msg( 'rev-deleted-user' )->escaped(); } if ( $this->file->isDeleted( File::DELETED_USER ) ) { return '<span class="history-deleted">' . $link . '</span>'; } 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->getAuthority() ) ) { $block = MediaWikiServices::getInstance()->getCommentFormatter() ->formatBlock( $this->file->getDescription() ); } else { $block = ' ' . $this->list->msg( 'rev-deleted-comment' )->escaped(); } if ( $this->file->isDeleted( File::DELETED_COMMENT ) ) { return "<span class=\"history-deleted\">$block</span>"; } return $block; } public function getHTML() { $data = $this->list->msg( 'widthheight' )->numParams( $this->file->getWidth(), $this->file->getHeight() )->escaped() . ' (' . $this->list->msg( 'nbytes' )->numParams( $this->file->getSize() )->escaped() . ')'; return '<li>' . $this->getLink() . ' ' . $this->getUserTools() . ' ' . $data . ' ' . $this->getComment() . '</li>'; } public function getApiData( ApiResult $result ) { $file = $this->file; $user = $this->list->getUser(); $ret = [ 'title' => $this->list->getPageName(), 'archivename' => $file->getArchiveName(), 'timestamp' => wfTimestamp( TS_ISO_8601, $file->getTimestamp() ), 'width' => $file->getWidth(), 'height' => $file->getHeight(), 'size' => $file->getSize(), 'userhidden' => (bool)$file->isDeleted( File::DELETED_USER ), 'commenthidden' => (bool)$file->isDeleted( File::DELETED_COMMENT ), 'contenthidden' => (bool)$this->isDeleted(), ]; if ( !$this->isDeleted() ) { $ret += [ 'url' => $file->getUrl(), ]; } elseif ( $this->canViewContent() ) { $ret += [ 'url' => SpecialPage::getTitleFor( 'Revisiondelete' )->getLinkURL( [ 'target' => $this->list->getPageName(), 'file' => $file->getArchiveName(), 'token' => $user->getEditToken( $file->getArchiveName() ) ] ), ]; } $uploader = $file->getUploader( File::FOR_THIS_USER, $user ); if ( $uploader ) { $ret += [ 'userid' => $uploader->getId(), 'user' => $uploader->getName(), ]; } $comment = $file->getDescription( File::FOR_THIS_USER, $user ); if ( ( $comment ?? '' ) !== '' ) { $ret += [ 'comment' => $comment, ]; } return $ret; } public function lock() { return $this->file->acquireFileLock(); } public function unlock() { return $this->file->releaseFileLock(); } } PK ! p)��� � RevDelArchiveItem.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\MediaWikiServices; use MediaWiki\SpecialPage\SpecialPage; use Wikimedia\Rdbms\IDBAccessObject; /** * Item class for a archive table row */ class RevDelArchiveItem extends RevDelRevisionItem { protected static function initRevisionRecord( $list, $row ) { $revRecord = MediaWikiServices::getInstance() ->getRevisionFactory() ->newRevisionFromArchiveRow( $row, IDBAccessObject::READ_NORMAL, null, [ 'page_id' => $list->getPage()->getId() ] ); return $revRecord; } 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 getAuthorActorField() { return 'ar_actor'; } public function getId() { # Convert DB timestamp to MW timestamp return $this->revisionRecord->getTimestamp(); } public function setBits( $bits ) { $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase(); $dbw->newUpdateQueryBuilder() ->update( 'archive' ) ->set( [ 'ar_deleted' => $bits ] ) ->where( [ 'ar_namespace' => $this->list->getPage()->getNamespace(), 'ar_title' => $this->list->getPage()->getDBkey(), // use timestamp for index 'ar_timestamp' => $this->row->ar_timestamp, 'ar_rev_id' => $this->row->ar_rev_id, 'ar_deleted' => $this->getBits() ] ) ->caller( __METHOD__ )->execute(); return (bool)$dbw->affectedRows(); } protected function getRevisionLink() { $date = $this->list->getLanguage()->userTimeAndDate( $this->revisionRecord->getTimestamp(), $this->list->getUser() ); if ( $this->isDeleted() && !$this->canViewContent() ) { return htmlspecialchars( $date ); } return $this->getLinkRenderer()->makeLink( SpecialPage::getTitleFor( 'Undelete' ), $date, [], [ 'target' => $this->list->getPageName(), 'timestamp' => $this->revisionRecord->getTimestamp() ] ); } protected function getDiffLink() { if ( $this->isDeleted() && !$this->canViewContent() ) { return $this->list->msg( 'diff' )->escaped(); } return $this->getLinkRenderer()->makeLink( SpecialPage::getTitleFor( 'Undelete' ), $this->list->msg( 'diff' )->text(), [], [ 'target' => $this->list->getPageName(), 'diff' => 'prev', 'timestamp' => $this->revisionRecord->getTimestamp() ] ); } } PK ! &C��0 �0 RevDelList.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\Context\IContextSource; use MediaWiki\Deferred\DeferredUpdates; use MediaWiki\Page\PageIdentity; use MediaWiki\Revision\RevisionRecord; use MediaWiki\RevisionList\RevisionListBase; use MediaWiki\Status\Status; use MediaWiki\Title\Title; use Wikimedia\Rdbms\LBFactory; /** * Abstract base class for a list of deletable items. The list class * needs to be able to make a query from a set of identifiers to pull * relevant rows, to return RevDelItem subclasses wrapping them, and * to wrap bulk update operations. * * @property RevDelItem $current * @method RevDelItem next() * @method RevDelItem reset() * @method RevDelItem current() */ abstract class RevDelList extends RevisionListBase { /** Flag used for suppression, depending on the type of log */ protected const SUPPRESS_BIT = RevisionRecord::DELETED_RESTRICTED; /** @var LBFactory */ private $lbFactory; /** * @param IContextSource $context * @param PageIdentity $page * @param array $ids * @param LBFactory $lbFactory */ public function __construct( IContextSource $context, PageIdentity $page, array $ids, LBFactory $lbFactory ) { parent::__construct( $context, $page ); // ids is a protected variable in RevisionListBase $this->ids = $ids; $this->lbFactory = $lbFactory; } /** * 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; } /** * Indicate whether any item in this list is suppressed * @since 1.25 * @return bool */ public function areAnySuppressed() { /** @var RevDelItem $item */ foreach ( $this as $item ) { if ( $item->getBits() & self::SUPPRESS_BIT ) { return true; } } return false; } /** * 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: ExtractBitParams() bitfield array * comment: The log comment * perItemStatus: Set if you want per-item status reports * tags: The array of change tags to apply to the log entry * @return Status * @since 1.23 Added 'perItemStatus' param */ public function setVisibility( array $params ) { $status = Status::newGood(); $bitPars = $params['value']; $comment = $params['comment']; $perItemStatus = $params['perItemStatus'] ?? false; // T387638 - Always ensure ->value['itemStatuses'] is set if requested if ( $perItemStatus ) { $status->value['itemStatuses'] = []; } // CAS-style checks are done on the _deleted fields so the select // does not need to use FOR UPDATE nor be in the atomic section $dbw = $this->lbFactory->getPrimaryDatabase(); $this->res = $this->doQuery( $dbw ); $status->merge( $this->acquireItemLocks() ); if ( !$status->isGood() ) { return $status; } $dbw->startAtomic( __METHOD__, $dbw::ATOMIC_CANCELABLE ); $dbw->onTransactionResolution( function () { // Release locks on commit or error $this->releaseItemLocks(); }, __METHOD__ ); $missing = array_fill_keys( $this->ids, true ); $this->clearFileOps(); $idsForLog = []; $authorActors = []; // For multi-item deletions, set the old/new bitfields in log_params such that "hid X" // shows in logs if field X was hidden from ANY item and likewise for "unhid Y". Note the // form does not let the same field get hidden and unhidden in different items at once. $virtualOldBits = 0; $virtualNewBits = 0; $logType = 'delete'; // Will be filled with id => [old, new bits] information and // passed to doPostCommitUpdates(). $visibilityChangeMap = []; /** @var RevDelItem $item */ foreach ( $this as $item ) { unset( $missing[$item->getId()] ); if ( $perItemStatus ) { $itemStatus = Status::newGood(); $status->value['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; } elseif ( !$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 } elseif ( $newBits == self::SUPPRESS_BIT ) { $itemStatus->warning( 'revdelete-only-restricted', $item->formatDate(), $item->formatTime() ); $status->failCount++; continue; } // Update the revision $ok = $item->setBits( $newBits ); if ( $ok ) { $idsForLog[] = $item->getId(); // If any item field was suppressed or unsuppressed if ( ( $oldBits | $newBits ) & self::SUPPRESS_BIT ) { $logType = 'suppress'; } // Track which fields where (un)hidden for each item $addedBits = ( $oldBits ^ $newBits ) & $newBits; $removedBits = ( $oldBits ^ $newBits ) & $oldBits; $virtualNewBits |= $addedBits; $virtualOldBits |= $removedBits; $status->successCount++; $authorActors[] = $item->getAuthorActor(); // Save the old and new bits in $visibilityChangeMap for // later use. $visibilityChangeMap[$item->getId()] = [ 'oldBits' => $oldBits, 'newBits' => $newBits, ]; } else { $itemStatus->error( 'revdelete-concurrent-change', $item->formatDate(), $item->formatTime() ); $status->failCount++; } } // Handle missing revisions foreach ( $missing as $id => $unused ) { if ( $perItemStatus ) { $status->value['itemStatuses'][$id] = Status::newFatal( 'revdelete-modify-missing', $id ); } else { $status->error( 'revdelete-modify-missing', $id ); } $status->failCount++; } if ( $status->successCount == 0 ) { $dbw->endAtomic( __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 or I/O failures $dbw->cancelAtomic( __METHOD__ ); return $status; } // Log it $authorFields = []; $authorFields['authorActors'] = $authorActors; $this->updateLog( $logType, [ 'page' => $this->page, 'count' => $successCount, 'newBits' => $virtualNewBits, 'oldBits' => $virtualOldBits, 'comment' => $comment, 'ids' => $idsForLog, 'tags' => $params['tags'] ?? [], ] + $authorFields ); // Clear caches after commit DeferredUpdates::addCallableUpdate( function () use ( $visibilityChangeMap ) { $this->doPostCommitUpdates( $visibilityChangeMap ); }, DeferredUpdates::PRESEND, $dbw ); $dbw->endAtomic( __METHOD__ ); return $status; } final protected function acquireItemLocks() { $status = Status::newGood(); /** @var RevDelItem $item */ foreach ( $this as $item ) { $status->merge( $item->lock() ); } return $status; } final protected function releaseItemLocks() { $status = Status::newGood(); /** @var RevDelItem $item */ foreach ( $this as $item ) { $status->merge( $item->unlock() ); } return $status; } /** * Reload the list data from the primary DB. This can be done after setVisibility() * to allow $item->getHTML() to show the new data. * @since 1.37 */ public function reloadFromPrimary() { $dbw = $this->lbFactory->getPrimaryDatabase(); $this->res = $this->doQuery( $dbw ); } /** * Record a log entry on the action * @param string $logType One of (delete,suppress) * @param array $params Associative array of parameters: * newBits: The new value of the *_deleted bitfield * oldBits: The old value of the *_deleted bitfield. * page: The target page reference * ids: The ID list * comment: The log comment * authorActors: The array of the actor IDs of the offenders * tags: The array of change tags to apply to the log entry */ private function updateLog( $logType, $params ) { // Get the URL param's corresponding DB field $field = RevisionDeleter::getRelationType( $this->getType() ); if ( !$field ) { throw new UnexpectedValueException( "Bad log URL param type!" ); } // Add params for affected page and ids $logParams = $this->getLogParams( $params ); // Actually add the deletion log entry $logEntry = new ManualLogEntry( $logType, $this->getLogAction() ); $logEntry->setTarget( $params['page'] ); $logEntry->setComment( $params['comment'] ); $logEntry->setParameters( $logParams ); $logEntry->setPerformer( $this->getUser() ); // Allow for easy searching of deletion log items for revision/log items $relations = [ $field => $params['ids'], ]; if ( isset( $params['authorActors'] ) ) { $relations += [ 'target_author_actor' => $params['authorActors'], ]; } $logEntry->setRelations( $relations ); // Apply change tags to the log entry $logEntry->addTags( $params['tags'] ); $logId = $logEntry->insert(); $logEntry->publish( $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 [ '4::type' => $this->getType(), '5::ids' => $params['ids'], '6::ofield' => $params['oldBits'], '7::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 * @param array $visibilityChangeMap [id => ['oldBits' => $oldBits, 'newBits' => $newBits], ... ] * @return Status */ public function doPostCommitUpdates( array $visibilityChangeMap ) { return Status::newGood(); } } PK ! ��*IR R RevDelItem.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\Api\ApiResult; use MediaWiki\RevisionList\RevisionItemBase; use MediaWiki\Status\Status; /** * Abstract base class for deletable items */ abstract class RevDelItem 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 * @param int $newBits * @return bool */ public function isHideCurrentOp( $newBits ) { return false; } /** * Get the current deletion bitfield value * * @return int */ 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. * * @param int $newBits * @return bool Success */ abstract public function setBits( $newBits ); /** * Get the return information about the revision for the API * @since 1.23 * @param ApiResult $result * @return array Data for the API result */ abstract public function getApiData( ApiResult $result ); /** * Lock the item against changes outside of the DB * @return Status * @since 1.28 */ public function lock() { return Status::newGood(); } /** * Unlock the item against changes outside of the DB * @return Status * @since 1.28 */ public function unlock() { return Status::newGood(); } } PK ! &�7 7 RevDelArchivedRevisionItem.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\RevisionList\RevisionListBase; use Wikimedia\Rdbms\IConnectionProvider; /** * Item class for a archive table row by ar_rev_id -- actually * used via RevDelRevisionList. */ class RevDelArchivedRevisionItem extends RevDelArchiveItem { /** @var IConnectionProvider */ protected IConnectionProvider $connectionProvider; /** * @param RevisionListBase $list * @param stdClass $row * @param IConnectionProvider $connectionProvider */ public function __construct( RevisionListBase $list, stdClass $row, IConnectionProvider $connectionProvider ) { $this->connectionProvider = $connectionProvider; parent::__construct( $list, $row ); } /** * @return string */ public function getIdField(): string { return 'ar_rev_id'; } /** * @return int */ public function getId(): int { return $this->getRevisionRecord()->getId(); } /** * @param int $bits * @return bool */ public function setBits( $bits ): bool { $dbw = $this->connectionProvider->getPrimaryDatabase(); $dbw->newUpdateQueryBuilder() ->update( 'archive' ) ->set( [ 'ar_deleted' => $bits ] ) ->where( [ 'ar_rev_id' => $this->row->ar_rev_id, 'ar_deleted' => $this->getBits(), ] ) ->caller( __METHOD__ )->execute(); return (bool)$dbw->affectedRows(); } } PK ! ���d d RevDelRevisionItem.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\Api\ApiResult; use MediaWiki\Linker\Linker; use MediaWiki\MediaWikiServices; use MediaWiki\Revision\RevisionRecord; use MediaWiki\RevisionList\RevisionListBase; use MediaWiki\Xml\Xml; /** * Item class for a live revision table row * * @property RevDelRevisionList $list */ class RevDelRevisionItem extends RevDelItem { /** @var RevisionRecord */ public $revisionRecord; public function __construct( RevisionListBase $list, $row ) { parent::__construct( $list, $row ); $this->revisionRecord = static::initRevisionRecord( $list, $row ); } /** * Create RevisionRecord object from $row sourced from $list * * @param RevisionListBase $list * @param mixed $row * @return RevisionRecord */ protected static function initRevisionRecord( $list, $row ) { return MediaWikiServices::getInstance() ->getRevisionFactory() ->newRevisionFromRow( $row ); } /** * Get the RevisionRecord for the item * * @return RevisionRecord */ protected function getRevisionRecord(): RevisionRecord { return $this->revisionRecord; } 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 getAuthorActorField() { return 'rev_actor'; } public function canView() { return $this->getRevisionRecord()->userCan( RevisionRecord::DELETED_RESTRICTED, $this->list->getAuthority() ); } public function canViewContent() { return $this->getRevisionRecord()->userCan( RevisionRecord::DELETED_TEXT, $this->list->getAuthority() ); } public function getBits() { return $this->getRevisionRecord()->getVisibility(); } public function setBits( $bits ) { $revRecord = $this->getRevisionRecord(); $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase(); // Update revision table $dbw->newUpdateQueryBuilder() ->update( 'revision' ) ->set( [ 'rev_deleted' => $bits ] ) ->where( [ 'rev_id' => $revRecord->getId(), 'rev_page' => $revRecord->getPageId(), 'rev_deleted' => $this->getBits() // cas ] ) ->caller( __METHOD__ )->execute(); if ( !$dbw->affectedRows() ) { // Concurrent fail! return false; } // Update recentchanges table $dbw->newUpdateQueryBuilder() ->update( 'recentchanges' ) ->set( [ 'rc_deleted' => $bits, 'rc_patrolled' => RecentChange::PRC_AUTOPATROLLED ] ) ->where( [ 'rc_this_oldid' => $revRecord->getId() ] ) ->caller( __METHOD__ )->execute(); return true; } public function isDeleted() { return $this->getRevisionRecord()->isDeleted( RevisionRecord::DELETED_TEXT ); } public function isHideCurrentOp( $newBits ) { return ( $newBits & RevisionRecord::DELETED_TEXT ) && $this->list->getCurrent() == $this->getId(); } /** * Get the HTML link to the revision text. * Overridden by RevDelArchiveItem. * @return string */ protected function getRevisionLink() { $date = $this->list->getLanguage()->userTimeAndDate( $this->getRevisionRecord()->getTimestamp(), $this->list->getUser() ); if ( $this->isDeleted() && !$this->canViewContent() ) { return htmlspecialchars( $date ); } return $this->getLinkRenderer()->makeKnownLink( $this->list->getPage(), $date, [], [ 'oldid' => $this->getRevisionRecord()->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 $this->getLinkRenderer()->makeKnownLink( $this->list->getPage(), $this->list->msg( 'diff' )->text(), [], [ 'diff' => $this->getRevisionRecord()->getId(), 'oldid' => 'prev', 'unhide' => 1 ] ); } } /** * @return string A HTML <li> element representing this revision, showing * change tags and everything */ public function getHTML() { $revRecord = $this->getRevisionRecord(); $difflink = $this->list->msg( 'parentheses' ) ->rawParams( $this->getDiffLink() )->escaped(); $revlink = $this->getRevisionLink(); $userlink = Linker::revUserLink( $revRecord ); $comment = MediaWikiServices::getInstance()->getCommentFormatter() ->formatRevision( $revRecord, $this->list->getAuthority() ); if ( $this->isDeleted() ) { $class = Linker::getRevisionDeletedClass( $revRecord ); $revlink = "<span class=\"$class\">$revlink</span>"; } $content = "$difflink $revlink $userlink $comment"; $attribs = []; $tags = $this->getTags(); if ( $tags ) { [ $tagSummary, $classes ] = ChangeTags::formatSummaryRow( $tags, 'revisiondelete', $this->list->getContext() ); $content .= " $tagSummary"; $attribs['class'] = implode( ' ', $classes ); } return Xml::tags( 'li', $attribs, $content ); } /** * @return string Comma-separated list of tags */ public function getTags() { return $this->row->ts_tags; } public function getApiData( ApiResult $result ) { $revRecord = $this->getRevisionRecord(); $authority = $this->list->getAuthority(); $ret = [ 'id' => $revRecord->getId(), 'timestamp' => wfTimestamp( TS_ISO_8601, $revRecord->getTimestamp() ), 'userhidden' => (bool)$revRecord->isDeleted( RevisionRecord::DELETED_USER ), 'commenthidden' => (bool)$revRecord->isDeleted( RevisionRecord::DELETED_COMMENT ), 'texthidden' => (bool)$revRecord->isDeleted( RevisionRecord::DELETED_TEXT ), ]; if ( $revRecord->userCan( RevisionRecord::DELETED_USER, $authority ) ) { $revUser = $revRecord->getUser( RevisionRecord::FOR_THIS_USER, $authority ); $ret += [ 'userid' => $revUser ? $revUser->getId() : 0, 'user' => $revUser ? $revUser->getName() : '', ]; } if ( $revRecord->userCan( RevisionRecord::DELETED_COMMENT, $authority ) ) { $revComment = $revRecord->getComment( RevisionRecord::FOR_THIS_USER, $authority ); $ret += [ 'comment' => $revComment ? $revComment->text : '' ]; } return $ret; } } PK ! yk�0 0 RevDelRevisionList.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\Cache\HTMLCacheUpdater; use MediaWiki\Context\IContextSource; use MediaWiki\HookContainer\HookContainer; use MediaWiki\HookContainer\HookRunner; use MediaWiki\MediaWikiServices; use MediaWiki\Page\PageIdentity; use MediaWiki\Revision\RevisionRecord; use MediaWiki\Revision\RevisionStore; use MediaWiki\Status\Status; use MediaWiki\Title\Title; use Wikimedia\Rdbms\FakeResultWrapper; use Wikimedia\Rdbms\IResultWrapper; use Wikimedia\Rdbms\LBFactory; /** * List for revision table items * * This will check both the 'revision' table for live revisions and the * 'archive' table for traditionally-deleted revisions that have an * ar_rev_id saved. * * See RevDelRevisionItem and RevDelArchivedRevisionItem for items. */ class RevDelRevisionList extends RevDelList { /** @var LBFactory */ private $lbFactory; /** @var HookRunner */ private $hookRunner; /** @var HTMLCacheUpdater */ private $htmlCacheUpdater; /** @var RevisionStore */ private $revisionStore; /** @var int */ public $currentRevId; /** * @param IContextSource $context * @param PageIdentity $page * @param array $ids * @param LBFactory $lbFactory * @param HookContainer $hookContainer * @param HTMLCacheUpdater $htmlCacheUpdater * @param RevisionStore $revisionStore */ public function __construct( IContextSource $context, PageIdentity $page, array $ids, LBFactory $lbFactory, HookContainer $hookContainer, HTMLCacheUpdater $htmlCacheUpdater, RevisionStore $revisionStore ) { parent::__construct( $context, $page, $ids, $lbFactory ); $this->lbFactory = $lbFactory; $this->hookRunner = new HookRunner( $hookContainer ); $this->htmlCacheUpdater = $htmlCacheUpdater; $this->revisionStore = $revisionStore; } public function getType() { return 'revision'; } public static function getRelationType() { return 'rev_id'; } public static function getRestriction() { return 'deleterevision'; } public static function getRevdelConstant() { return RevisionRecord::DELETED_TEXT; } public static function suggestTarget( $target, array $ids ) { $revisionRecord = MediaWikiServices::getInstance() ->getRevisionLookup() ->getRevisionById( $ids[0] ); if ( $revisionRecord ) { return Title::newFromLinkTarget( $revisionRecord->getPageAsLinkTarget() ); } return $target; } /** * @param \Wikimedia\Rdbms\IReadableDatabase $db * @return IResultWrapper */ public function doQuery( $db ) { $ids = array_map( 'intval', $this->ids ); $queryBuilder = $this->revisionStore->newSelectQueryBuilder( $db ) ->joinComment() ->joinUser() ->joinPage() ->where( [ 'rev_page' => $this->page->getId(), 'rev_id' => $ids ] ) ->orderBy( 'rev_id', \Wikimedia\Rdbms\SelectQueryBuilder::SORT_DESC ) // workaround for MySQL bug (T104313) ->useIndex( [ 'revision' => 'PRIMARY' ] ); MediaWikiServices::getInstance()->getChangeTagsStore()->modifyDisplayQueryBuilder( $queryBuilder, 'revision' ); $live = $queryBuilder->caller( __METHOD__ )->fetchResultSet(); if ( $live->numRows() >= count( $ids ) ) { // All requested revisions are live, keeps things simple! return $live; } $queryBuilder = $this->revisionStore->newArchiveSelectQueryBuilder( $db ) ->joinComment() ->where( [ 'ar_rev_id' => $ids ] ) ->orderBy( 'ar_rev_id', \Wikimedia\Rdbms\SelectQueryBuilder::SORT_DESC ); MediaWikiServices::getInstance()->getChangeTagsStore()->modifyDisplayQueryBuilder( $queryBuilder, 'archive' ); // Check if any requested revisions are available fully deleted. $archived = $queryBuilder->caller( __METHOD__ )->fetchResultSet(); if ( $archived->numRows() == 0 ) { return $live; } elseif ( $live->numRows() == 0 ) { return $archived; } else { // Combine the two! Whee $rows = []; 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, $this->lbFactory ); } else { // This shouldn't happen. :) throw new InvalidArgumentException( 'Invalid row type in RevDelRevisionList' ); } } public function getCurrent() { if ( $this->currentRevId === null ) { $dbw = $this->lbFactory->getPrimaryDatabase(); $this->currentRevId = $dbw->newSelectQueryBuilder() ->select( 'page_latest' ) ->from( 'page' ) ->where( [ 'page_namespace' => $this->page->getNamespace(), 'page_title' => $this->page->getDBkey() ] ) ->caller( __METHOD__ )->fetchField(); } return $this->currentRevId; } public function doPreCommitUpdates() { Title::newFromPageIdentity( $this->page )->invalidateCache(); return Status::newGood(); } public function doPostCommitUpdates( array $visibilityChangeMap ) { $this->htmlCacheUpdater->purgeTitleUrls( $this->page, HTMLCacheUpdater::PURGE_INTENT_TXROUND_REFLECTED ); // Extensions that require referencing previous revisions may need this $this->hookRunner->onArticleRevisionVisibilitySet( Title::newFromPageIdentity( $this->page ), $this->ids, $visibilityChangeMap ); return Status::newGood(); } } PK ! ���� � RevisionDeleteUser.phpnu �Iw�� <?php /** * Backend functions for suppressing and unsuppressing all references to a given user. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\MediaWikiServices; use MediaWiki\Revision\RevisionRecord; use MediaWiki\Title\Title; use Wikimedia\Rdbms\IDatabase; use Wikimedia\Rdbms\RawSQLValue; /** * Backend functions for suppressing and unsuppressing all references to a given user, * used when blocking with HideUser enabled. This was spun out of SpecialBlockip.php * in 1.18; at some point it needs to be rewritten to either use RevisionDelete abstraction, * or at least schema abstraction. * * @ingroup RevisionDelete */ class RevisionDeleteUser { /** * Update *_deleted bitfields in various tables to hide or unhide usernames * * @param string $name Username * @param int $userId * @param string $op Operator '|' or '&' * @param null|IDatabase $dbw If you happen to have one lying around * @return bool True on success, false on failure (e.g. invalid user ID) */ private static function setUsernameBitfields( $name, $userId, $op, ?IDatabase $dbw = null ) { if ( !$userId || ( $op !== '|' && $op !== '&' ) ) { return false; } if ( !$dbw instanceof IDatabase ) { $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase(); } # To suppress, we OR the current bitfields with RevisionRecord::DELETED_USER # to put a 1 in the username *_deleted bit. To unsuppress we AND the # current bitfields with the inverse of RevisionRecord::DELETED_USER. The # username bit is made to 0 (x & 0 = 0), while others are unchanged (x & 1 = x). # The same goes for the sysop-restricted *_deleted bit. $delUser = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED; $delAction = LogPage::DELETED_ACTION | RevisionRecord::DELETED_RESTRICTED; if ( $op === '&' ) { $delUser = $dbw->bitNot( $delUser ); $delAction = $dbw->bitNot( $delAction ); } # Normalize user name $userTitle = Title::makeTitleSafe( NS_USER, $name ); $userDbKey = $userTitle->getDBkey(); $actorId = $dbw->newSelectQueryBuilder() ->select( 'actor_id' ) ->from( 'actor' ) ->where( [ 'actor_name' => $name ] ) ->caller( __METHOD__ )->fetchField(); if ( $actorId ) { # Hide name from live edits $dbw->newUpdateQueryBuilder() ->update( 'revision' ) ->set( self::buildSetBitDeletedField( 'rev_deleted', $op, $delUser, $dbw ) ) ->where( [ 'rev_actor' => $actorId ] ) ->caller( __METHOD__ )->execute(); # Hide name from deleted edits $dbw->newUpdateQueryBuilder() ->update( 'archive' ) ->set( self::buildSetBitDeletedField( 'ar_deleted', $op, $delUser, $dbw ) ) ->where( [ 'ar_actor' => $actorId ] ) ->caller( __METHOD__ )->execute(); # Hide name from logs $dbw->newUpdateQueryBuilder() ->update( 'logging' ) ->set( self::buildSetBitDeletedField( 'log_deleted', $op, $delUser, $dbw ) ) ->where( [ 'log_actor' => $actorId, $dbw->expr( 'log_type', '!=', 'suppress' ) ] ) ->caller( __METHOD__ )->execute(); # Hide name from RC $dbw->newUpdateQueryBuilder() ->update( 'recentchanges' ) ->set( self::buildSetBitDeletedField( 'rc_deleted', $op, $delUser, $dbw ) ) ->where( [ 'rc_actor' => $actorId ] ) ->caller( __METHOD__ )->execute(); # Hide name from live images $dbw->newUpdateQueryBuilder() ->update( 'oldimage' ) ->set( self::buildSetBitDeletedField( 'oi_deleted', $op, $delUser, $dbw ) ) ->where( [ 'oi_actor' => $actorId ] ) ->caller( __METHOD__ )->execute(); # Hide name from deleted images $dbw->newUpdateQueryBuilder() ->update( 'filearchive' ) ->set( self::buildSetBitDeletedField( 'fa_deleted', $op, $delUser, $dbw ) ) ->where( [ 'fa_actor' => $actorId ] ) ->caller( __METHOD__ )->execute(); } # Hide log entries pointing to the user page $dbw->newUpdateQueryBuilder() ->update( 'logging' ) ->set( self::buildSetBitDeletedField( 'log_deleted', $op, $delAction, $dbw ) ) ->where( [ 'log_namespace' => NS_USER, 'log_title' => $userDbKey, $dbw->expr( 'log_type', '!=', 'suppress' ) ] ) ->caller( __METHOD__ )->execute(); # Hide RC entries pointing to the user page $dbw->newUpdateQueryBuilder() ->update( 'recentchanges' ) ->set( self::buildSetBitDeletedField( 'rc_deleted', $op, $delAction, $dbw ) ) ->where( [ 'rc_namespace' => NS_USER, 'rc_title' => $userDbKey, $dbw->expr( 'rc_logid', '>', 0 ) ] ) ->caller( __METHOD__ )->execute(); return true; } private static function buildSetBitDeletedField( $field, $op, $value, IDatabase $dbw ) { return [ $field => new RawSQLValue( $op === '&' ? $dbw->bitAnd( $field, $value ) : $dbw->bitOr( $field, $value ) ) ]; } /** * @param string $name User name * @param int $userId Both user name and ID must be provided * @param IDatabase|null $dbw If you happen to have one lying around * @return bool True on success, false on failure (e.g. invalid user ID) */ public static function suppressUserName( $name, $userId, ?IDatabase $dbw = null ) { return self::setUsernameBitfields( $name, $userId, '|', $dbw ); } /** * @param string $name User name * @param int $userId Both user name and ID must be provided * @param IDatabase|null $dbw If you happen to have one lying around * @return bool True on success, false on failure (e.g. invalid user ID) */ public static function unsuppressUserName( $name, $userId, ?IDatabase $dbw = null ) { return self::setUsernameBitfields( $name, $userId, '&', $dbw ); } } PK ! 64�� � ) Hook/ArticleRevisionVisibilitySetHook.phpnu �Iw�� <?php namespace MediaWiki\Hook; use MediaWiki\Title\Title; /** * This is a hook handler interface, see docs/Hooks.md. * Use the hook name "ArticleRevisionVisibilitySet" to register handlers implementing this interface. * * @stable to implement * @ingroup Hooks */ interface ArticleRevisionVisibilitySetHook { /** * This hook is called when changing visibility of one or more * revisions of an article. * * @since 1.35 * * @param Title $title Title of the article * @param int[] $ids IDs to set the visibility for * @param array $visibilityChangeMap Map of revision ID to oldBits and newBits. * This array can be examined to determine exactly what visibility bits * have changed for each revision. This array is of the form: * [id => ['oldBits' => $oldBits, 'newBits' => $newBits], ... ] * @return bool|void True or no return value to continue or false to abort */ public function onArticleRevisionVisibilitySet( $title, $ids, $visibilityChangeMap ); } PK ! W�� RevDelLogList.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\CommentStore\CommentStore; use MediaWiki\Context\IContextSource; use MediaWiki\MediaWikiServices; use MediaWiki\Page\PageIdentity; use MediaWiki\SpecialPage\SpecialPage; use Wikimedia\Rdbms\IResultWrapper; use Wikimedia\Rdbms\LBFactory; use Wikimedia\Rdbms\SelectQueryBuilder; /** * List for logging table items */ class RevDelLogList extends RevDelList { protected const SUPPRESS_BIT = LogPage::DELETED_RESTRICTED; /** @var CommentStore */ private $commentStore; private LogFormatterFactory $logFormatterFactory; /** * @internal Use RevisionDeleter * @param IContextSource $context * @param PageIdentity $page * @param array $ids * @param LBFactory $lbFactory * @param CommentStore $commentStore * @param LogFormatterFactory $logFormatterFactory */ public function __construct( IContextSource $context, PageIdentity $page, array $ids, LBFactory $lbFactory, CommentStore $commentStore, LogFormatterFactory $logFormatterFactory ) { parent::__construct( $context, $page, $ids, $lbFactory ); $this->commentStore = $commentStore; $this->logFormatterFactory = $logFormatterFactory; } 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 ) { $dbr = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase(); $result = $dbr->newSelectQueryBuilder() ->select( 'log_type' ) ->distinct() ->from( 'logging' ) ->where( [ 'log_id' => $ids ] ) ->caller( __METHOD__ )->fetchResultSet(); 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 \Wikimedia\Rdbms\IReadableDatabase $db * @return IResultWrapper */ public function doQuery( $db ) { $ids = array_map( 'intval', $this->ids ); $queryBuilder = $db->newSelectQueryBuilder() ->select( [ 'log_id', 'log_type', 'log_action', 'log_timestamp', 'log_actor', 'log_namespace', 'log_title', 'log_page', 'log_params', 'log_deleted', 'log_user' => 'actor_user', 'log_user_text' => 'actor_name', 'log_comment_text' => 'comment_log_comment.comment_text', 'log_comment_data' => 'comment_log_comment.comment_data', 'log_comment_cid' => 'comment_log_comment.comment_id' ] ) ->from( 'logging' ) ->join( 'actor', null, 'actor_id=log_actor' ) ->join( 'comment', 'comment_log_comment', 'comment_log_comment.comment_id = log_comment_id' ) ->where( [ 'log_id' => $ids ] ) ->orderBy( [ 'log_timestamp', 'log_id' ], SelectQueryBuilder::SORT_DESC ); MediaWikiServices::getInstance()->getChangeTagsStore()->modifyDisplayQueryBuilder( $queryBuilder, 'logging' ); return $queryBuilder->caller( __METHOD__ )->fetchResultSet(); } public function newItem( $row ) { return new RevDelLogItem( $this, $row, $this->commentStore, MediaWikiServices::getInstance()->getConnectionProvider(), $this->logFormatterFactory ); } public function getLogAction() { return 'event'; } public function getLogParams( $params ) { return [ '4::ids' => $params['ids'], '5::ofield' => $params['oldBits'], '6::nfield' => $params['newBits'], ]; } } PK ! Y��! ! RevDelFileList.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\Cache\HTMLCacheUpdater; use MediaWiki\Context\IContextSource; use MediaWiki\FileRepo\File\FileSelectQueryBuilder; use MediaWiki\Page\PageIdentity; use MediaWiki\Status\Status; use Wikimedia\Rdbms\IReadableDatabase; use Wikimedia\Rdbms\IResultWrapper; use Wikimedia\Rdbms\LBFactory; use Wikimedia\Rdbms\SelectQueryBuilder; /** * List for oldimage table items */ class RevDelFileList extends RevDelList { protected const SUPPRESS_BIT = File::DELETED_RESTRICTED; /** @var HTMLCacheUpdater */ private $htmlCacheUpdater; /** @var RepoGroup */ private $repoGroup; /** @var array */ public $storeBatch; /** @var array */ public $deleteBatch; /** @var array */ public $cleanupBatch; /** * @param IContextSource $context * @param PageIdentity $page * @param array $ids * @param LBFactory $lbFactory * @param HTMLCacheUpdater $htmlCacheUpdater * @param RepoGroup $repoGroup */ public function __construct( IContextSource $context, PageIdentity $page, array $ids, LBFactory $lbFactory, HTMLCacheUpdater $htmlCacheUpdater, RepoGroup $repoGroup ) { parent::__construct( $context, $page, $ids, $lbFactory ); $this->htmlCacheUpdater = $htmlCacheUpdater; $this->repoGroup = $repoGroup; } 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; } /** * @param IReadableDatabase $db * @return IResultWrapper */ public function doQuery( $db ) { $archiveNames = []; foreach ( $this->ids as $timestamp ) { $archiveNames[] = $timestamp . '!' . $this->page->getDBkey(); } $queryBuilder = FileSelectQueryBuilder::newForOldFile( $db ); $queryBuilder ->where( [ 'oi_name' => $this->page->getDBkey(), 'oi_archive_name' => $archiveNames ] ) ->orderBy( 'oi_timestamp', SelectQueryBuilder::SORT_DESC ); return $queryBuilder->caller( __METHOD__ )->fetchResultSet(); } public function newItem( $row ) { return new RevDelFileItem( $this, $row ); } public function clearFileOps() { $this->deleteBatch = []; $this->storeBatch = []; $this->cleanupBatch = []; } public function doPreCommitUpdates() { $status = Status::newGood(); $repo = $this->repoGroup->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( array $visibilityChangeMap ) { $file = $this->repoGroup->getLocalRepo()->newFile( $this->page ); $file->purgeCache(); $file->purgeDescription(); // Purge full images from cache $purgeUrls = []; foreach ( $this->ids as $timestamp ) { $archiveName = $timestamp . '!' . $this->page->getDBkey(); $file->purgeOldThumbnails( $archiveName ); $purgeUrls[] = $file->getArchiveUrl( $archiveName ); } $this->htmlCacheUpdater->purgeUrls( $purgeUrls, HTMLCacheUpdater::PURGE_INTENT_TXROUND_REFLECTED ); return Status::newGood(); } } PK ! .�'� � RevDelLogItem.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\Api\ApiResult; use MediaWiki\CommentStore\CommentStore; use MediaWiki\Html\Html; use MediaWiki\RevisionList\RevisionListBase; use MediaWiki\SpecialPage\SpecialPage; use MediaWiki\Title\Title; use MediaWiki\Xml\Xml; use Wikimedia\Rdbms\IConnectionProvider; /** * Item class for a logging table row */ class RevDelLogItem extends RevDelItem { /** @var CommentStore */ private $commentStore; private IConnectionProvider $dbProvider; private LogFormatterFactory $logFormatterFactory; /** * @param RevisionListBase $list * @param stdClass $row DB result row * @param CommentStore $commentStore * @param IConnectionProvider $dbProvider * @param LogFormatterFactory $logFormatterFactory */ public function __construct( RevisionListBase $list, $row, CommentStore $commentStore, IConnectionProvider $dbProvider, LogFormatterFactory $logFormatterFactory ) { parent::__construct( $list, $row ); $this->commentStore = $commentStore; $this->dbProvider = $dbProvider; $this->logFormatterFactory = $logFormatterFactory; } 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 getAuthorActorField() { return 'log_actor'; } public function canView() { return LogEventsList::userCan( $this->row, LogPage::DELETED_RESTRICTED, $this->list->getAuthority() ); } public function canViewContent() { return true; // none } public function getBits() { return (int)$this->row->log_deleted; } public function setBits( $bits ) { $dbw = $this->dbProvider->getPrimaryDatabase(); $dbw->newUpdateQueryBuilder() ->update( 'logging' ) ->set( [ 'log_deleted' => $bits ] ) ->where( [ 'log_id' => $this->row->log_id, 'log_deleted' => $this->getBits() // cas ] ) ->caller( __METHOD__ )->execute(); if ( !$dbw->affectedRows() ) { // Concurrent fail! return false; } $dbw->newUpdateQueryBuilder() ->update( 'recentchanges' ) ->set( [ 'rc_deleted' => $bits, 'rc_patrolled' => RecentChange::PRC_AUTOPATROLLED ] ) ->where( [ 'rc_logid' => $this->row->log_id, 'rc_timestamp' => $this->row->log_timestamp // index ] ) ->caller( __METHOD__ )->execute(); return true; } 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 = $this->logFormatterFactory->newFromRow( $this->row ); $formatter->setContext( $this->list->getContext() ); $formatter->setAudience( LogFormatter::FOR_THIS_USER ); // Log link for this page $loglink = $this->getLinkRenderer()->makeLink( SpecialPage::getTitleFor( 'Log' ), $this->list->msg( 'log' )->text(), [], [ 'page' => $title->getPrefixedText() ] ); $loglink = $this->list->msg( 'parentheses' )->rawParams( $loglink )->escaped(); // User links and action text $action = $formatter->getActionText(); $dir = $this->list->getLanguage()->getDir(); $comment = Html::rawElement( 'bdi', [ 'dir' => $dir ], $formatter->getComment() ); $content = "$loglink $date $action $comment"; $attribs = []; if ( $this->row->ts_tags ) { [ $tagSummary, $classes ] = ChangeTags::formatSummaryRow( $this->row->ts_tags, 'revisiondelete', $this->list->getContext() ); $content .= " $tagSummary"; $attribs['class'] = implode( ' ', $classes ); } return Xml::tags( 'li', $attribs, $content ); } public function getApiData( ApiResult $result ) { $logEntry = DatabaseLogEntry::newFromRow( $this->row ); $user = $this->list->getAuthority(); $ret = [ 'id' => $logEntry->getId(), 'type' => $logEntry->getType(), 'action' => $logEntry->getSubtype(), 'userhidden' => (bool)$logEntry->isDeleted( LogPage::DELETED_USER ), 'commenthidden' => (bool)$logEntry->isDeleted( LogPage::DELETED_COMMENT ), 'actionhidden' => (bool)$logEntry->isDeleted( LogPage::DELETED_ACTION ), ]; if ( LogEventsList::userCan( $this->row, LogPage::DELETED_ACTION, $user ) ) { $ret['params'] = $this->logFormatterFactory->newFromEntry( $logEntry )->formatParametersForApi(); } if ( LogEventsList::userCan( $this->row, LogPage::DELETED_USER, $user ) ) { $ret += [ 'userid' => $this->row->log_user ?? 0, 'user' => $this->row->log_user_text, ]; } if ( LogEventsList::userCan( $this->row, LogPage::DELETED_COMMENT, $user ) ) { $ret += [ 'comment' => $this->commentStore->getComment( 'log_comment', $this->row )->text, ]; } return $ret; } } PK ! �7�J J RevDelArchivedFileList.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\Cache\HTMLCacheUpdater; use MediaWiki\Context\IContextSource; use MediaWiki\FileRepo\File\FileSelectQueryBuilder; use MediaWiki\Page\PageIdentity; use Wikimedia\Rdbms\IReadableDatabase; use Wikimedia\Rdbms\IResultWrapper; use Wikimedia\Rdbms\LBFactory; use Wikimedia\Rdbms\SelectQueryBuilder; /** * List for filearchive table items */ class RevDelArchivedFileList extends RevDelFileList { /** * @param IContextSource $context * @param PageIdentity $page * @param array $ids * @param LBFactory $lbFactory * @param HTMLCacheUpdater $htmlCacheUpdater * @param RepoGroup $repoGroup */ public function __construct( IContextSource $context, PageIdentity $page, array $ids, LBFactory $lbFactory, HTMLCacheUpdater $htmlCacheUpdater, RepoGroup $repoGroup ) { parent::__construct( $context, $page, $ids, $lbFactory, $htmlCacheUpdater, $repoGroup ); // Technically, we could just inherit the constructor from RevDelFileList, // but since ArchivedFile::getQueryInfo() uses MediaWikiServices it might // be useful to replace at some point with either a callback or a separate // service to allow for unit testing } public function getType() { return 'filearchive'; } public static function getRelationType() { return 'fa_id'; } /** * @param IReadableDatabase $db * @return IResultWrapper */ public function doQuery( $db ) { $ids = array_map( 'intval', $this->ids ); $queryBuilder = FileSelectQueryBuilder::newForArchivedFile( $db ); $queryBuilder->where( [ 'fa_name' => $this->page->getDBkey(), 'fa_id' => $ids ] ) ->orderBy( 'fa_id', SelectQueryBuilder::SORT_DESC ); return $queryBuilder->caller( __METHOD__ )->fetchResultSet(); } public function newItem( $row ) { return new RevDelArchivedFileItem( $this, $row ); } } PK ! 4a6 6 RevDelArchiveList.phpnu �Iw�� <?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\Cache\HTMLCacheUpdater; use MediaWiki\Context\IContextSource; use MediaWiki\HookContainer\HookContainer; use MediaWiki\MediaWikiServices; use MediaWiki\Page\PageIdentity; use MediaWiki\Revision\RevisionStore; use MediaWiki\Status\Status; use Wikimedia\Rdbms\IResultWrapper; use Wikimedia\Rdbms\LBFactory; use Wikimedia\Rdbms\SelectQueryBuilder; /** * List for archive table items, i.e. revisions deleted via action=delete */ class RevDelArchiveList extends RevDelRevisionList { /** @var RevisionStore */ private $revisionStore; /** * @param IContextSource $context * @param PageIdentity $page * @param array $ids * @param LBFactory $lbFactory * @param HookContainer $hookContainer * @param HTMLCacheUpdater $htmlCacheUpdater * @param RevisionStore $revisionStore */ public function __construct( IContextSource $context, PageIdentity $page, array $ids, LBFactory $lbFactory, HookContainer $hookContainer, HTMLCacheUpdater $htmlCacheUpdater, RevisionStore $revisionStore ) { parent::__construct( $context, $page, $ids, $lbFactory, $hookContainer, $htmlCacheUpdater, $revisionStore ); $this->revisionStore = $revisionStore; } public function getType() { return 'archive'; } public static function getRelationType() { return 'ar_timestamp'; } /** * @param \Wikimedia\Rdbms\IReadableDatabase $db * @return IResultWrapper */ public function doQuery( $db ) { $timestamps = []; foreach ( $this->ids as $id ) { $timestamps[] = $db->timestamp( $id ); } $queryBuilder = $this->revisionStore->newArchiveSelectQueryBuilder( $db ) ->joinComment() ->where( [ 'ar_namespace' => $this->getPage()->getNamespace(), 'ar_title' => $this->getPage()->getDBkey(), 'ar_timestamp' => $timestamps, ] ) ->orderBy( 'ar_timestamp', SelectQueryBuilder::SORT_DESC ); MediaWikiServices::getInstance()->getChangeTagsStore()->modifyDisplayQueryBuilder( $queryBuilder, 'archive' ); return $queryBuilder->caller( __METHOD__ )->fetchResultSet(); } public function newItem( $row ) { return new RevDelArchiveItem( $this, $row ); } public function doPreCommitUpdates() { return Status::newGood(); } public function doPostCommitUpdates( array $visibilityChangeMap ) { return Status::newGood(); } } PK ! '�<� � RevisionDeleter.phpnu �Iw�� <?php /** * Revision/log/file deletion backend * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup RevisionDelete */ use MediaWiki\Context\IContextSource; use MediaWiki\MediaWikiServices; use MediaWiki\Page\PageIdentity; use MediaWiki\Revision\RevisionRecord; use MediaWiki\Title\Title; /** * General controller for RevDel, used by both SpecialRevisiondelete and * ApiRevisionDelete. * @ingroup RevisionDelete */ class RevisionDeleter { /** * List of known revdel types, with their corresponding ObjectFactory spec to * create the relevant class. All specs need to include DBLoadBalancerFactory, * which is used in the base RevDelList class */ private const ALLOWED_TYPES = [ 'revision' => [ 'class' => RevDelRevisionList::class, 'services' => [ 'DBLoadBalancerFactory', 'HookContainer', 'HtmlCacheUpdater', 'RevisionStore', ], ], 'archive' => [ 'class' => RevDelArchiveList::class, 'services' => [ 'DBLoadBalancerFactory', 'HookContainer', 'HtmlCacheUpdater', 'RevisionStore', ], ], 'oldimage' => [ 'class' => RevDelFileList::class, 'services' => [ 'DBLoadBalancerFactory', 'HtmlCacheUpdater', 'RepoGroup', ], ], 'filearchive' => [ 'class' => RevDelArchivedFileList::class, 'services' => [ 'DBLoadBalancerFactory', 'HtmlCacheUpdater', 'RepoGroup', ], ], 'logging' => [ 'class' => RevDelLogList::class, 'services' => [ 'DBLoadBalancerFactory', 'CommentStore', 'LogFormatterFactory', ], ], ]; /** Type map to support old log entries */ private const DEPRECATED_TYPE_MAP = [ 'oldid' => 'revision', 'artimestamp' => 'archive', 'oldimage' => 'oldimage', 'fileid' => 'filearchive', 'logid' => 'logging', ]; /** * Lists the valid possible types for revision deletion. * * @since 1.22 * @return array */ public static function getTypes() { return array_keys( self::ALLOWED_TYPES ); } /** * Gets the canonical type name, if any. * * @since 1.22 * @param string $typeName * @return string|null */ public static function getCanonicalTypeName( $typeName ) { if ( isset( self::DEPRECATED_TYPE_MAP[$typeName] ) ) { $typeName = self::DEPRECATED_TYPE_MAP[$typeName]; } return isset( self::ALLOWED_TYPES[$typeName] ) ? $typeName : null; } /** * Instantiate the appropriate list class for a given list of IDs. * * @since 1.22 * @param string $typeName RevDel type, see RevisionDeleter::getTypes() * @param IContextSource $context * @param PageIdentity $page * @param array $ids * @return RevDelList */ public static function createList( $typeName, IContextSource $context, PageIdentity $page, array $ids ) { $typeName = self::getCanonicalTypeName( $typeName ); if ( !$typeName ) { throw new InvalidArgumentException( __METHOD__ . ": Unknown RevDel type '$typeName'" ); } $spec = self::ALLOWED_TYPES[$typeName]; $objectFactory = MediaWikiServices::getInstance()->getObjectFactory(); // ObjectFactory::createObject accepts an array, not just a callable (phan bug) // @phan-suppress-next-line PhanTypeInvalidCallableArrayKey return $objectFactory->createObject( $spec, [ 'extraArgs' => [ $context, $page, $ids ], 'assertClass' => RevDelList::class, ] ); } /** * 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 * enabled / disabled. * @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 ) { $arr[( $new & $field ) ? 0 : 1][] = $desc; } } /** * Gets an array of message keys describing the changes made to the * visibility of the revision. * * If the resulting array is $arr, then $arr[0] will contain an array of * keys describing the items that were hidden, $arr[1] will contain * an array of keys describing the items that were unhidden, and $arr[2] * will contain an array with a single message key, which can be one of * "revdelete-restricted", "revdelete-unrestricted" indicating (un)suppression * or null to indicate nothing in particular. * You can turn the keys in $arr[0] and $arr[1] into message keys by * appending -hid and -unhid to the keys respectively. * * @param int $n The new bitfield. * @param int $o The old bitfield. * @return array An array as described above. * @since 1.19 public */ public static function getChanges( $n, $o ) { $diff = $n ^ $o; $ret = [ 0 => [], 1 => [], 2 => [] ]; // Build bitfield changes in language self::checkItem( 'revdelete-content', RevisionRecord::DELETED_TEXT, $diff, $n, $ret ); self::checkItem( 'revdelete-summary', RevisionRecord::DELETED_COMMENT, $diff, $n, $ret ); self::checkItem( 'revdelete-uname', RevisionRecord::DELETED_USER, $diff, $n, $ret ); // Restriction application to sysops if ( $diff & RevisionRecord::DELETED_RESTRICTED ) { if ( $n & RevisionRecord::DELETED_RESTRICTED ) { $ret[2][] = 'revdelete-restricted'; } else { $ret[2][] = 'revdelete-unrestricted'; } } return $ret; } /** Get DB field name for URL param... * Future code for other things may also track * other types of revision-specific changes. * @param string $typeName * @return string|null One of log_id/rev_id/fa_id/ar_timestamp/oi_archive_name */ public static function getRelationType( $typeName ) { $typeName = self::getCanonicalTypeName( $typeName ); if ( !$typeName ) { return null; } return call_user_func( [ self::ALLOWED_TYPES[$typeName]['class'], 'getRelationType' ] ); } /** * Get the user right required for the RevDel type * @since 1.22 * @param string $typeName * @return string|null User right */ public static function getRestriction( $typeName ) { $typeName = self::getCanonicalTypeName( $typeName ); if ( !$typeName ) { return null; } return call_user_func( [ self::ALLOWED_TYPES[$typeName]['class'], 'getRestriction' ] ); } /** * Get the revision deletion constant for the RevDel type * @since 1.22 * @param string $typeName * @return int|null RevDel constant */ public static function getRevdelConstant( $typeName ) { $typeName = self::getCanonicalTypeName( $typeName ); if ( !$typeName ) { return null; } return call_user_func( [ self::ALLOWED_TYPES[$typeName]['class'], 'getRevdelConstant' ] ); } /** * Suggest a target for the revision deletion * @since 1.22 * @param string $typeName * @param Title|null $target User-supplied target * @param array $ids * @return Title|null */ public static function suggestTarget( $typeName, $target, array $ids ) { $typeName = self::getCanonicalTypeName( $typeName ); if ( !$typeName ) { return $target; } return call_user_func( [ self::ALLOWED_TYPES[$typeName]['class'], 'suggestTarget' ], $target, $ids ); } /** * Put together a rev_deleted bitfield * @since 1.22 * @param array $bitPars ExtractBitParams() params * @param int $oldfield Current bitfield * @return int */ public static function extractBitfield( array $bitPars, $oldfield ) { // Build the actual new rev_deleted bitfield $newBits = 0; foreach ( $bitPars as $const => $val ) { if ( $val == 1 ) { $newBits |= $const; // $const is the *_deleted const } elseif ( $val == -1 ) { $newBits |= ( $oldfield & $const ); // use existing } } return $newBits; } } PK ! �A�� � RevDelArchivedFileItem.phpnu �Iw�� PK ! 1_��f f � RevDelFileItem.phpnu �Iw�� PK ! p)��� � |/ RevDelArchiveItem.phpnu �Iw�� PK ! &C��0 �0 �<