X-Git-Url: https://scripts.mit.edu/gitweb/autoinstallsdev/mediawiki.git/blobdiff_plain/19e297c21b10b1b8a3acad5e73fc71dcb35db44a..6932310fd58ebef145fa01eb76edf7150284d8ea:/includes/api/ApiQueryDeletedrevs.php diff --git a/includes/api/ApiQueryDeletedrevs.php b/includes/api/ApiQueryDeletedrevs.php index 936e5365..5dd007b4 100644 --- a/includes/api/ApiQueryDeletedrevs.php +++ b/includes/api/ApiQueryDeletedrevs.php @@ -1,10 +1,10 @@ .@home.nl + * Copyright © 2007 Roan Kattouw ".@gmail.com" * * 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 @@ -24,50 +24,65 @@ * @file */ -if ( !defined( 'MEDIAWIKI' ) ) { - // Eclipse helper - will be ignored in production - require_once( 'ApiQueryBase.php' ); -} - /** * Query module to enumerate all deleted revisions. * * @ingroup API + * @deprecated since 1.25 */ class ApiQueryDeletedrevs extends ApiQueryBase { - public function __construct( $query, $moduleName ) { + public function __construct( ApiQuery $query, $moduleName ) { parent::__construct( $query, $moduleName, 'dr' ); } public function execute() { - global $wgUser; // Before doing anything at all, let's check permissions - if ( !$wgUser->isAllowed( 'deletedhistory' ) ) { - $this->dieUsage( 'You don\'t have permission to view deleted revision information', 'permissiondenied' ); - } + $this->checkUserRightsAny( 'deletedhistory' ); + + $this->addDeprecation( 'apiwarn-deprecation-deletedrevs', 'action=query&list=deletedrevs' ); + $user = $this->getUser(); $db = $this->getDB(); + $commentStore = new CommentStore( 'ar_comment' ); $params = $this->extractRequestParams( false ); $prop = array_flip( $params['prop'] ); + $fld_parentid = isset( $prop['parentid'] ); $fld_revid = isset( $prop['revid'] ); $fld_user = isset( $prop['user'] ); $fld_userid = isset( $prop['userid'] ); $fld_comment = isset( $prop['comment'] ); - $fld_parsedcomment = isset ( $prop['parsedcomment'] ); + $fld_parsedcomment = isset( $prop['parsedcomment'] ); $fld_minor = isset( $prop['minor'] ); $fld_len = isset( $prop['len'] ); + $fld_sha1 = isset( $prop['sha1'] ); $fld_content = isset( $prop['content'] ); $fld_token = isset( $prop['token'] ); + $fld_tags = isset( $prop['tags'] ); + + if ( isset( $prop['token'] ) ) { + $p = $this->getModulePrefix(); + } + + // If we're in a mode that breaks the same-origin policy, no tokens can + // be obtained + if ( $this->lacksSameOriginSecurity() ) { + $fld_token = false; + } + + // If user can't undelete, no tokens + if ( !$user->isAllowed( 'undelete' ) ) { + $fld_token = false; + } $result = $this->getResult(); $pageSet = $this->getPageSet(); $titles = $pageSet->getTitles(); // This module operates in three modes: - // 'revs': List deleted revs for certain titles - // 'user': List deleted revs by a certain user - // 'all': List all deleted revs + // 'revs': List deleted revs for certain titles (1) + // 'user': List deleted revs by a certain user (2) + // 'all': List all deleted revs in NS (3) $mode = 'all'; if ( count( $titles ) > 0 ) { $mode = 'revs'; @@ -75,59 +90,95 @@ class ApiQueryDeletedrevs extends ApiQueryBase { $mode = 'user'; } + if ( $mode == 'revs' || $mode == 'user' ) { + // Ignore namespace and unique due to inability to know whether they were purposely set + foreach ( [ 'from', 'to', 'prefix', /*'namespace', 'unique'*/ ] as $p ) { + if ( !is_null( $params[$p] ) ) { + $this->dieWithError( [ 'apierror-deletedrevs-param-not-1-2', $p ], 'badparams' ); + } + } + } else { + foreach ( [ 'start', 'end' ] as $p ) { + if ( !is_null( $params[$p] ) ) { + $this->dieWithError( [ 'apierror-deletedrevs-param-not-3', $p ], 'badparams' ); + } + } + } + if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) { - $this->dieUsage( 'user and excludeuser cannot be used together', 'badparams' ); + $this->dieWithError( 'user and excludeuser cannot be used together', 'badparams' ); } $this->addTables( 'archive' ); - $this->addWhere( 'ar_deleted = 0' ); - $this->addFields( array( 'ar_title', 'ar_namespace', 'ar_timestamp' ) ); - if ( $fld_revid ) { - $this->addFields( 'ar_rev_id' ); - } - if ( $fld_user ) { - $this->addFields( 'ar_user_text' ); - } - if ( $fld_userid ) { - $this->addFields( 'ar_user' ); - } + $this->addFields( [ 'ar_title', 'ar_namespace', 'ar_timestamp', 'ar_deleted', 'ar_id' ] ); + + $this->addFieldsIf( 'ar_parent_id', $fld_parentid ); + $this->addFieldsIf( 'ar_rev_id', $fld_revid ); + $this->addFieldsIf( 'ar_user_text', $fld_user ); + $this->addFieldsIf( 'ar_user', $fld_userid ); + $this->addFieldsIf( 'ar_minor_edit', $fld_minor ); + $this->addFieldsIf( 'ar_len', $fld_len ); + $this->addFieldsIf( 'ar_sha1', $fld_sha1 ); + if ( $fld_comment || $fld_parsedcomment ) { - $this->addFields( 'ar_comment' ); + $commentQuery = $commentStore->getJoin(); + $this->addTables( $commentQuery['tables'] ); + $this->addFields( $commentQuery['fields'] ); + $this->addJoinConds( $commentQuery['joins'] ); } - if ( $fld_minor ) { - $this->addFields( 'ar_minor_edit' ); + + if ( $fld_tags ) { + $this->addTables( 'tag_summary' ); + $this->addJoinConds( + [ 'tag_summary' => [ 'LEFT JOIN', [ 'ar_rev_id=ts_rev_id' ] ] ] + ); + $this->addFields( 'ts_tags' ); } - if ( $fld_len ) { - $this->addFields( 'ar_len' ); + + if ( !is_null( $params['tag'] ) ) { + $this->addTables( 'change_tag' ); + $this->addJoinConds( + [ 'change_tag' => [ 'INNER JOIN', [ 'ar_rev_id=ct_rev_id' ] ] ] + ); + $this->addWhereFld( 'ct_tag', $params['tag'] ); } + if ( $fld_content ) { + // Modern MediaWiki has the content for deleted revs in the 'text' + // table using fields old_text and old_flags. But revisions deleted + // pre-1.5 store the content in the 'archive' table directly using + // fields ar_text and ar_flags, and no corresponding 'text' row. So + // we have to LEFT JOIN and fetch all four fields, plus ar_text_id + // to be able to tell the difference. $this->addTables( 'text' ); - $this->addFields( array( 'ar_text', 'ar_text_id', 'old_text', 'old_flags' ) ); - $this->addWhere( 'ar_text_id = old_id' ); + $this->addJoinConds( + [ 'text' => [ 'LEFT JOIN', [ 'ar_text_id=old_id' ] ] ] + ); + $this->addFields( [ 'ar_text', 'ar_flags', 'ar_text_id', 'old_text', 'old_flags' ] ); // This also means stricter restrictions - if ( !$wgUser->isAllowed( 'undelete' ) ) { - $this->dieUsage( 'You don\'t have permission to view deleted revision content', 'permissiondenied' ); - } + $this->checkUserRightsAny( [ 'deletedtext', 'undelete' ] ); } // Check limits $userMax = $fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1; - $botMax = $fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2; + $botMax = $fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2; $limit = $params['limit']; if ( $limit == 'max' ) { $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; - $this->getResult()->setParsedLimit( $this->getModuleName(), $limit ); + $this->getResult()->addParsedLimit( $this->getModuleName(), $limit ); } $this->validateLimit( 'limit', $limit, 1, $userMax, $botMax ); if ( $fld_token ) { // Undelete tokens are identical for all pages, so we cache one here - $token = $wgUser->editToken(); + $token = $user->getEditToken( '', $this->getMain()->getRequest() ); } + $dir = $params['dir']; + // We need a custom WHERE clause that matches all titles. if ( $mode == 'revs' ) { $lb = new LinkBatch( $titles ); @@ -135,9 +186,19 @@ class ApiQueryDeletedrevs extends ApiQueryBase { $this->addWhere( $where ); } elseif ( $mode == 'all' ) { $this->addWhereFld( 'ar_namespace', $params['namespace'] ); - if ( !is_null( $params['from'] ) ) { - $from = $this->getDB()->strencode( $this->titleToKey( $params['from'] ) ); - $this->addWhere( "ar_title >= '$from'" ); + + $from = $params['from'] === null + ? null + : $this->titlePartToKey( $params['from'], $params['namespace'] ); + $to = $params['to'] === null + ? null + : $this->titlePartToKey( $params['to'], $params['namespace'] ); + $this->addWhereRange( 'ar_title', $dir, $from, $to ); + + if ( isset( $params['prefix'] ) ) { + $this->addWhere( 'ar_title' . $db->buildLike( + $this->titlePartToKey( $params['prefix'], $params['namespace'] ), + $db->anyString() ) ); } } @@ -145,233 +206,313 @@ class ApiQueryDeletedrevs extends ApiQueryBase { $this->addWhereFld( 'ar_user_text', $params['user'] ); } elseif ( !is_null( $params['excludeuser'] ) ) { $this->addWhere( 'ar_user_text != ' . - $this->getDB()->addQuotes( $params['excludeuser'] ) ); + $db->addQuotes( $params['excludeuser'] ) ); } - if ( !is_null( $params['continue'] ) && ( $mode == 'all' || $mode == 'revs' ) ) - { - $cont = explode( '|', $params['continue'] ); - if ( count( $cont ) != 3 ) { - $this->dieUsage( 'Invalid continue param. You should pass the original value returned by the previous query', 'badcontinue' ); + if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) { + // Paranoia: avoid brute force searches (T19342) + // (shouldn't be able to get here without 'deletedhistory', but + // check it again just in case) + if ( !$user->isAllowed( 'deletedhistory' ) ) { + $bitmask = Revision::DELETED_USER; + } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) { + $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED; + } else { + $bitmask = 0; } - $ns = intval( $cont[0] ); - $title = $this->getDB()->strencode( $this->titleToKey( $cont[1] ) ); - $ts = $this->getDB()->strencode( $cont[2] ); - $op = ( $params['dir'] == 'newer' ? '>' : '<' ); - $this->addWhere( "ar_namespace $op $ns OR " . + if ( $bitmask ) { + $this->addWhere( $db->bitAnd( 'ar_deleted', $bitmask ) . " != $bitmask" ); + } + } + + if ( !is_null( $params['continue'] ) ) { + $cont = explode( '|', $params['continue'] ); + $op = ( $dir == 'newer' ? '>' : '<' ); + if ( $mode == 'all' || $mode == 'revs' ) { + $this->dieContinueUsageIf( count( $cont ) != 4 ); + $ns = intval( $cont[0] ); + $this->dieContinueUsageIf( strval( $ns ) !== $cont[0] ); + $title = $db->addQuotes( $cont[1] ); + $ts = $db->addQuotes( $db->timestamp( $cont[2] ) ); + $ar_id = (int)$cont[3]; + $this->dieContinueUsageIf( strval( $ar_id ) !== $cont[3] ); + $this->addWhere( "ar_namespace $op $ns OR " . "(ar_namespace = $ns AND " . - "(ar_title $op '$title' OR " . - "(ar_title = '$title' AND " . - "ar_timestamp $op= '$ts')))" ); + "(ar_title $op $title OR " . + "(ar_title = $title AND " . + "(ar_timestamp $op $ts OR " . + "(ar_timestamp = $ts AND " . + "ar_id $op= $ar_id)))))" ); + } else { + $this->dieContinueUsageIf( count( $cont ) != 2 ); + $ts = $db->addQuotes( $db->timestamp( $cont[0] ) ); + $ar_id = (int)$cont[1]; + $this->dieContinueUsageIf( strval( $ar_id ) !== $cont[1] ); + $this->addWhere( "ar_timestamp $op $ts OR " . + "(ar_timestamp = $ts AND " . + "ar_id $op= $ar_id)" ); + } } $this->addOption( 'LIMIT', $limit + 1 ); - $this->addOption( 'USE INDEX', array( 'archive' => ( $mode == 'user' ? 'usertext_timestamp' : 'name_title_timestamp' ) ) ); + $this->addOption( + 'USE INDEX', + [ 'archive' => ( $mode == 'user' ? 'ar_usertext_timestamp' : 'name_title_timestamp' ) ] + ); if ( $mode == 'all' ) { if ( $params['unique'] ) { + // @todo Does this work on non-MySQL? $this->addOption( 'GROUP BY', 'ar_title' ); - $this->addOption( 'ORDER BY', 'ar_title' ); } else { - $this->addOption( 'ORDER BY', 'ar_title, ar_timestamp' ); + $sort = ( $dir == 'newer' ? '' : ' DESC' ); + $this->addOption( 'ORDER BY', [ + 'ar_title' . $sort, + 'ar_timestamp' . $sort, + 'ar_id' . $sort, + ] ); } } else { if ( $mode == 'revs' ) { // Sort by ns and title in the same order as timestamp for efficiency - $this->addWhereRange( 'ar_namespace', $params['dir'], null, null ); - $this->addWhereRange( 'ar_title', $params['dir'], null, null ); + $this->addWhereRange( 'ar_namespace', $dir, null, null ); + $this->addWhereRange( 'ar_title', $dir, null, null ); } - $this->addWhereRange( 'ar_timestamp', $params['dir'], $params['start'], $params['end'] ); + $this->addTimestampWhereRange( 'ar_timestamp', $dir, $params['start'], $params['end'] ); + // Include in ORDER BY for uniqueness + $this->addWhereRange( 'ar_id', $dir, null, null ); } $res = $this->select( __METHOD__ ); - $pageMap = array(); // Maps ns&title to (fake) pageid + $pageMap = []; // Maps ns&title to (fake) pageid $count = 0; $newPageID = 0; foreach ( $res as $row ) { if ( ++$count > $limit ) { // We've had enough if ( $mode == 'all' || $mode == 'revs' ) { - $this->setContinueEnumParameter( 'continue', intval( $row->ar_namespace ) . '|' . - $this->keyToTitle( $row->ar_title ) . '|' . $row->ar_timestamp ); + $this->setContinueEnumParameter( 'continue', + "$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id" + ); } else { - $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ar_timestamp ) ); + $this->setContinueEnumParameter( 'continue', "$row->ar_timestamp|$row->ar_id" ); } break; } - $rev = array(); + $rev = []; + $anyHidden = false; + $rev['timestamp'] = wfTimestamp( TS_ISO_8601, $row->ar_timestamp ); if ( $fld_revid ) { $rev['revid'] = intval( $row->ar_rev_id ); } - if ( $fld_user ) { - $rev['user'] = $row->ar_user_text; + if ( $fld_parentid && !is_null( $row->ar_parent_id ) ) { + $rev['parentid'] = intval( $row->ar_parent_id ); } - if ( $fld_userid ) { - $rev['userid'] = $row->ar_user; - } - if ( $fld_comment ) { - $rev['comment'] = $row->ar_comment; + if ( $fld_user || $fld_userid ) { + if ( $row->ar_deleted & Revision::DELETED_USER ) { + $rev['userhidden'] = true; + $anyHidden = true; + } + if ( Revision::userCanBitfield( $row->ar_deleted, Revision::DELETED_USER, $user ) ) { + if ( $fld_user ) { + $rev['user'] = $row->ar_user_text; + } + if ( $fld_userid ) { + $rev['userid'] = (int)$row->ar_user; + } + } } - $title = Title::makeTitle( $row->ar_namespace, $row->ar_title ); - - if ( $fld_parsedcomment ) { - $rev['parsedcomment'] = $wgUser->getSkin()->formatComment( $row->ar_comment, $title ); + if ( $fld_comment || $fld_parsedcomment ) { + if ( $row->ar_deleted & Revision::DELETED_COMMENT ) { + $rev['commenthidden'] = true; + $anyHidden = true; + } + if ( Revision::userCanBitfield( $row->ar_deleted, Revision::DELETED_COMMENT, $user ) ) { + $comment = $commentStore->getComment( $row )->text; + if ( $fld_comment ) { + $rev['comment'] = $comment; + } + if ( $fld_parsedcomment ) { + $title = Title::makeTitle( $row->ar_namespace, $row->ar_title ); + $rev['parsedcomment'] = Linker::formatComment( $comment, $title ); + } + } } - if ( $fld_minor && $row->ar_minor_edit == 1 ) { - $rev['minor'] = ''; + + if ( $fld_minor ) { + $rev['minor'] = $row->ar_minor_edit == 1; } if ( $fld_len ) { $rev['len'] = $row->ar_len; } + if ( $fld_sha1 ) { + if ( $row->ar_deleted & Revision::DELETED_TEXT ) { + $rev['sha1hidden'] = true; + $anyHidden = true; + } + if ( Revision::userCanBitfield( $row->ar_deleted, Revision::DELETED_TEXT, $user ) ) { + if ( $row->ar_sha1 != '' ) { + $rev['sha1'] = Wikimedia\base_convert( $row->ar_sha1, 36, 16, 40 ); + } else { + $rev['sha1'] = ''; + } + } + } if ( $fld_content ) { - ApiResult::setContent( $rev, Revision::getRevisionText( $row ) ); + if ( $row->ar_deleted & Revision::DELETED_TEXT ) { + $rev['texthidden'] = true; + $anyHidden = true; + } + if ( Revision::userCanBitfield( $row->ar_deleted, Revision::DELETED_TEXT, $user ) ) { + if ( isset( $row->ar_text ) && !$row->ar_text_id ) { + // Pre-1.5 ar_text row (if condition from Revision::newFromArchiveRow) + ApiResult::setContentValue( $rev, 'text', Revision::getRevisionText( $row, 'ar_' ) ); + } else { + ApiResult::setContentValue( $rev, 'text', Revision::getRevisionText( $row ) ); + } + } + } + + if ( $fld_tags ) { + if ( $row->ts_tags ) { + $tags = explode( ',', $row->ts_tags ); + ApiResult::setIndexedTagName( $tags, 'tag' ); + $rev['tags'] = $tags; + } else { + $rev['tags'] = []; + } + } + + if ( $anyHidden && ( $row->ar_deleted & Revision::DELETED_RESTRICTED ) ) { + $rev['suppressed'] = true; } if ( !isset( $pageMap[$row->ar_namespace][$row->ar_title] ) ) { $pageID = $newPageID++; $pageMap[$row->ar_namespace][$row->ar_title] = $pageID; - $a['revisions'] = array( $rev ); - $result->setIndexedTagName( $a['revisions'], 'rev' ); + $a['revisions'] = [ $rev ]; + ApiResult::setIndexedTagName( $a['revisions'], 'rev' ); + $title = Title::makeTitle( $row->ar_namespace, $row->ar_title ); ApiQueryBase::addTitleInfo( $a, $title ); if ( $fld_token ) { $a['token'] = $token; } - $fit = $result->addValue( array( 'query', $this->getModuleName() ), $pageID, $a ); + $fit = $result->addValue( [ 'query', $this->getModuleName() ], $pageID, $a ); } else { $pageID = $pageMap[$row->ar_namespace][$row->ar_title]; $fit = $result->addValue( - array( 'query', $this->getModuleName(), $pageID, 'revisions' ), + [ 'query', $this->getModuleName(), $pageID, 'revisions' ], null, $rev ); } if ( !$fit ) { if ( $mode == 'all' || $mode == 'revs' ) { - $this->setContinueEnumParameter( 'continue', intval( $row->ar_namespace ) . '|' . - $this->keyToTitle( $row->ar_title ) . '|' . $row->ar_timestamp ); + $this->setContinueEnumParameter( 'continue', + "$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id" + ); } else { - $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ar_timestamp ) ); + $this->setContinueEnumParameter( 'continue', "$row->ar_timestamp|$row->ar_id" ); } break; } } - $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'page' ); + $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'page' ); + } + + public function isDeprecated() { + return true; } public function getAllowedParams() { - return array( - 'start' => array( - ApiBase::PARAM_TYPE => 'timestamp' - ), - 'end' => array( + return [ + 'start' => [ + ApiBase::PARAM_TYPE => 'timestamp', + ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 1, 2 ] ], + ], + 'end' => [ ApiBase::PARAM_TYPE => 'timestamp', - ), - 'dir' => array( - ApiBase::PARAM_TYPE => array( + ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 1, 2 ] ], + ], + 'dir' => [ + ApiBase::PARAM_TYPE => [ 'newer', 'older' - ), - ApiBase::PARAM_DFLT => 'older' - ), - 'from' => null, - 'continue' => null, - 'unique' => false, - 'user' => array( + ], + ApiBase::PARAM_DFLT => 'older', + ApiBase::PARAM_HELP_MSG => 'api-help-param-direction', + ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 1, 3 ] ], + ], + 'from' => [ + ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ], + ], + 'to' => [ + ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ], + ], + 'prefix' => [ + ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ], + ], + 'unique' => [ + ApiBase::PARAM_DFLT => false, + ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ], + ], + 'namespace' => [ + ApiBase::PARAM_TYPE => 'namespace', + ApiBase::PARAM_DFLT => NS_MAIN, + ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ], + ], + 'tag' => null, + 'user' => [ ApiBase::PARAM_TYPE => 'user' - ), - 'excludeuser' => array( + ], + 'excludeuser' => [ ApiBase::PARAM_TYPE => 'user' - ), - 'namespace' => array( - ApiBase::PARAM_TYPE => 'namespace', - ApiBase::PARAM_DFLT => 0, - ), - 'limit' => array( - ApiBase::PARAM_DFLT => 10, - ApiBase::PARAM_TYPE => 'limit', - ApiBase::PARAM_MIN => 1, - ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, - ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 - ), - 'prop' => array( + ], + 'prop' => [ ApiBase::PARAM_DFLT => 'user|comment', - ApiBase::PARAM_TYPE => array( + ApiBase::PARAM_TYPE => [ 'revid', + 'parentid', 'user', 'userid', 'comment', 'parsedcomment', 'minor', 'len', + 'sha1', 'content', - 'token' - ), + 'token', + 'tags' + ], ApiBase::PARAM_ISMULTI => true - ), - ); - } - - public function getParamDescription() { - return array( - 'start' => 'The timestamp to start enumerating from (1,2)', - 'end' => 'The timestamp to stop enumerating at (1,2)', - 'dir' => 'The direction in which to enumerate (1,2)', - 'limit' => 'The maximum amount of revisions to list', - 'prop' => array( - 'Which properties to get', - ' revid - Adds the revision id of the deleted revision', - ' user - Adds the user who made the revision', - ' userid - Adds the user id whom made the revision', - ' comment - Adds the comment of the revision', - ' parsedcomment - Adds the parsed comment of the revision', - ' minor - Tags if the revision is minor', - ' len - Adds the length of the revision', - ' content - Adds the content of the revision', - ' token - Gives the edit token', - ), - 'namespace' => 'Only list pages in this namespace (3)', - 'user' => 'Only list revisions by this user', - 'excludeuser' => 'Don\'t list revisions by this user', - 'from' => 'Start listing at this title (3)', - 'continue' => 'When more results are available, use this to continue (3)', - 'unique' => 'List only one revision for each page (3)', - ); - } - - public function getDescription() { - return array( - 'List deleted revisions.', - 'This module operates in three modes:', - '1) List deleted revisions for the given title(s), sorted by timestamp', - '2) List deleted contributions for the given user, sorted by timestamp (no titles specified)', - '3) List all deleted revisions in the given namespace, sorted by title and timestamp (no titles specified, druser not set)', - 'Certain parameters only apply to some modes and are ignored in others.', - 'For instance, a parameter marked (1) only applies to mode 1 and is ignored in modes 2 and 3', - ); - } - - public function getPossibleErrors() { - return array_merge( parent::getPossibleErrors(), array( - array( 'code' => 'permissiondenied', 'info' => 'You don\'t have permission to view deleted revision information' ), - array( 'code' => 'badparams', 'info' => 'user and excludeuser cannot be used together' ), - array( 'code' => 'permissiondenied', 'info' => 'You don\'t have permission to view deleted revision content' ), - array( 'code' => 'badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ), - ) ); + ], + 'limit' => [ + ApiBase::PARAM_DFLT => 10, + ApiBase::PARAM_TYPE => 'limit', + ApiBase::PARAM_MIN => 1, + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 + ], + 'continue' => [ + ApiBase::PARAM_HELP_MSG => 'api-help-param-continue', + ], + ]; } - protected function getExamples() { - return array( - 'List the last deleted revisions of Main Page and Talk:Main Page, with content (mode 1):', - ' api.php?action=query&list=deletedrevs&titles=Main%20Page|Talk:Main%20Page&drprop=user|comment|content', - 'List the last 50 deleted contributions by Bob (mode 2):', - ' api.php?action=query&list=deletedrevs&druser=Bob&drlimit=50', - 'List the first 50 deleted revisions in the main namespace (mode 3):', - ' api.php?action=query&list=deletedrevs&drdir=newer&drlimit=50', - 'List the first 50 deleted pages in the Talk namespace (mode 3):', - ' api.php?action=query&list=deletedrevs&drdir=newer&drlimit=50&drnamespace=1&drunique=', - ); + protected function getExamplesMessages() { + return [ + 'action=query&list=deletedrevs&titles=Main%20Page|Talk:Main%20Page&' . + 'drprop=user|comment|content' + => 'apihelp-query+deletedrevs-example-mode1', + 'action=query&list=deletedrevs&druser=Bob&drlimit=50' + => 'apihelp-query+deletedrevs-example-mode2', + 'action=query&list=deletedrevs&drdir=newer&drlimit=50' + => 'apihelp-query+deletedrevs-example-mode3-main', + 'action=query&list=deletedrevs&drdir=newer&drlimit=50&drnamespace=1&drunique=' + => 'apihelp-query+deletedrevs-example-mode3-talk', + ]; } - public function getVersion() { - return __CLASS__ . ': $Id$'; + public function getHelpUrls() { + return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Deletedrevs'; } }