Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 408 |
|
0.00% |
0 / 6 |
CRAP | |
0.00% |
0 / 1 |
| ApiQueryDeletedrevs | |
0.00% |
0 / 407 |
|
0.00% |
0 / 6 |
8742 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
| execute | |
0.00% |
0 / 293 |
|
0.00% |
0 / 1 |
7482 | |||
| isDeprecated | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getAllowedParams | |
0.00% |
0 / 86 |
|
0.00% |
0 / 1 |
6 | |||
| getExamplesMessages | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
6 | |||
| getHelpUrls | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * Copyright © 2007 Roan Kattouw <roan.kattouw@gmail.com> |
| 4 | * |
| 5 | * @license GPL-2.0-or-later |
| 6 | * @file |
| 7 | */ |
| 8 | |
| 9 | namespace MediaWiki\Api; |
| 10 | |
| 11 | use MediaWiki\ChangeTags\ChangeTagsStore; |
| 12 | use MediaWiki\CommentFormatter\RowCommentFormatter; |
| 13 | use MediaWiki\CommentStore\CommentStore; |
| 14 | use MediaWiki\Page\LinkBatchFactory; |
| 15 | use MediaWiki\ParamValidator\TypeDef\UserDef; |
| 16 | use MediaWiki\Revision\RevisionRecord; |
| 17 | use MediaWiki\Revision\RevisionStore; |
| 18 | use MediaWiki\Revision\SlotRecord; |
| 19 | use MediaWiki\Storage\NameTableAccessException; |
| 20 | use MediaWiki\Storage\NameTableStore; |
| 21 | use MediaWiki\Title\Title; |
| 22 | use Wikimedia\ParamValidator\ParamValidator; |
| 23 | use Wikimedia\ParamValidator\TypeDef\EnumDef; |
| 24 | use Wikimedia\ParamValidator\TypeDef\IntegerDef; |
| 25 | use Wikimedia\Rdbms\IExpression; |
| 26 | use Wikimedia\Rdbms\LikeValue; |
| 27 | use Wikimedia\Rdbms\Subquery; |
| 28 | use Wikimedia\Timestamp\TimestampFormat as TS; |
| 29 | |
| 30 | /** |
| 31 | * Query module to enumerate all deleted revisions. |
| 32 | * |
| 33 | * @ingroup API |
| 34 | * @deprecated since 1.25 |
| 35 | */ |
| 36 | class ApiQueryDeletedrevs extends ApiQueryBase { |
| 37 | |
| 38 | private CommentStore $commentStore; |
| 39 | private RowCommentFormatter $commentFormatter; |
| 40 | private RevisionStore $revisionStore; |
| 41 | private NameTableStore $changeTagDefStore; |
| 42 | private ChangeTagsStore $changeTagsStore; |
| 43 | private LinkBatchFactory $linkBatchFactory; |
| 44 | |
| 45 | public function __construct( |
| 46 | ApiQuery $query, |
| 47 | string $moduleName, |
| 48 | CommentStore $commentStore, |
| 49 | RowCommentFormatter $commentFormatter, |
| 50 | RevisionStore $revisionStore, |
| 51 | NameTableStore $changeTagDefStore, |
| 52 | ChangeTagsStore $changeTagsStore, |
| 53 | LinkBatchFactory $linkBatchFactory |
| 54 | ) { |
| 55 | parent::__construct( $query, $moduleName, 'dr' ); |
| 56 | $this->commentStore = $commentStore; |
| 57 | $this->commentFormatter = $commentFormatter; |
| 58 | $this->revisionStore = $revisionStore; |
| 59 | $this->changeTagDefStore = $changeTagDefStore; |
| 60 | $this->changeTagsStore = $changeTagsStore; |
| 61 | $this->linkBatchFactory = $linkBatchFactory; |
| 62 | } |
| 63 | |
| 64 | public function execute() { |
| 65 | // Before doing anything at all, let's check permissions |
| 66 | $this->checkUserRightsAny( 'deletedhistory' ); |
| 67 | |
| 68 | $this->addDeprecation( 'apiwarn-deprecation-deletedrevs', 'action=query&list=deletedrevs' ); |
| 69 | |
| 70 | $user = $this->getUser(); |
| 71 | $db = $this->getDB(); |
| 72 | $params = $this->extractRequestParams( false ); |
| 73 | $prop = array_fill_keys( $params['prop'], true ); |
| 74 | $fld_parentid = isset( $prop['parentid'] ); |
| 75 | $fld_revid = isset( $prop['revid'] ); |
| 76 | $fld_user = isset( $prop['user'] ); |
| 77 | $fld_userid = isset( $prop['userid'] ); |
| 78 | $fld_comment = isset( $prop['comment'] ); |
| 79 | $fld_parsedcomment = isset( $prop['parsedcomment'] ); |
| 80 | $fld_minor = isset( $prop['minor'] ); |
| 81 | $fld_len = isset( $prop['len'] ); |
| 82 | $fld_sha1 = isset( $prop['sha1'] ); |
| 83 | $fld_content = isset( $prop['content'] ); |
| 84 | $fld_token = isset( $prop['token'] ); |
| 85 | $fld_tags = isset( $prop['tags'] ); |
| 86 | |
| 87 | // If we're in a mode that breaks the same-origin policy, no tokens can |
| 88 | // be obtained |
| 89 | if ( $this->lacksSameOriginSecurity() || |
| 90 | // If user can't undelete, no tokens |
| 91 | !$this->getAuthority()->isAllowed( 'undelete' ) |
| 92 | ) { |
| 93 | $fld_token = false; |
| 94 | } |
| 95 | |
| 96 | $result = $this->getResult(); |
| 97 | $pageSet = $this->getPageSet(); |
| 98 | $titles = $pageSet->getPages(); |
| 99 | |
| 100 | // This module operates in three modes: |
| 101 | // 'revs': List deleted revs for certain titles (1) |
| 102 | // 'user': List deleted revs by a certain user (2) |
| 103 | // 'all': List all deleted revs in NS (3) |
| 104 | $mode = 'all'; |
| 105 | if ( count( $titles ) > 0 ) { |
| 106 | $mode = 'revs'; |
| 107 | } elseif ( $params['user'] !== null ) { |
| 108 | $mode = 'user'; |
| 109 | } |
| 110 | |
| 111 | if ( $mode == 'revs' || $mode == 'user' ) { |
| 112 | // Ignore namespace and unique due to inability to know whether they were purposely set |
| 113 | foreach ( [ 'from', 'to', 'prefix', /*'namespace', 'unique'*/ ] as $p ) { |
| 114 | if ( $params[$p] !== null ) { |
| 115 | $this->dieWithError( [ 'apierror-deletedrevs-param-not-1-2', $p ], 'badparams' ); |
| 116 | } |
| 117 | } |
| 118 | } else { |
| 119 | foreach ( [ 'start', 'end' ] as $p ) { |
| 120 | if ( $params[$p] !== null ) { |
| 121 | $this->dieWithError( [ 'apierror-deletedrevs-param-not-3', $p ], 'badparams' ); |
| 122 | } |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | if ( $params['user'] !== null && $params['excludeuser'] !== null ) { |
| 127 | $this->dieWithError( 'user and excludeuser cannot be used together', 'badparams' ); |
| 128 | } |
| 129 | |
| 130 | $arQuery = $this->revisionStore->getArchiveQueryInfo(); |
| 131 | $this->addTables( $arQuery['tables'] ); |
| 132 | $this->addFields( $arQuery['fields'] ); |
| 133 | $this->addJoinConds( $arQuery['joins'] ); |
| 134 | $this->addFields( [ 'ar_title', 'ar_namespace' ] ); |
| 135 | |
| 136 | if ( $fld_tags ) { |
| 137 | $this->addFields( [ |
| 138 | 'ts_tags' => $this->changeTagsStore->makeTagSummarySubquery( 'archive' ) |
| 139 | ] ); |
| 140 | } |
| 141 | |
| 142 | if ( $fld_sha1 ) { |
| 143 | $pairExpr = $db->buildGroupConcat( |
| 144 | $db->buildConcat( [ 'sr.role_name', $db->addQuotes( ':' ), 'c.content_sha1' ] ), |
| 145 | ',' |
| 146 | ); |
| 147 | $sha1Subquery = $db->newSelectQueryBuilder() |
| 148 | ->select( [ |
| 149 | 'ar_rev_id', |
| 150 | 'ar_deleted', |
| 151 | 'ar_slot_pairs' => $pairExpr, |
| 152 | ] ) |
| 153 | ->from( 'archive' ) |
| 154 | ->join( 'slots', 's', [ 'ar_rev_id = s.slot_revision_id' ] ) |
| 155 | ->join( 'content', 'c', [ 's.slot_content_id = c.content_id' ] ) |
| 156 | ->join( 'slot_roles', 'sr', [ 's.slot_role_id = sr.role_id' ] ) |
| 157 | ->groupBy( [ 'ar_rev_id', 'ar_deleted' ] ) |
| 158 | ->caller( __METHOD__ ) |
| 159 | ->getSQL(); |
| 160 | |
| 161 | $this->addTables( [ 'arsha1' => new Subquery( $sha1Subquery ) ] ); |
| 162 | $this->addFields( [ |
| 163 | 'ar_deleted' => 'arsha1.ar_deleted', |
| 164 | 'ar_slot_pairs' => 'arsha1.ar_slot_pairs' |
| 165 | ] ); |
| 166 | $this->addJoinConds( [ 'arsha1' => [ 'LEFT JOIN', [ 'ar_rev_id = arsha1.ar_rev_id' ] ] ] ); |
| 167 | } |
| 168 | |
| 169 | if ( $params['tag'] !== null ) { |
| 170 | $this->addTables( 'change_tag' ); |
| 171 | $this->addJoinConds( |
| 172 | [ 'change_tag' => [ 'JOIN', [ 'ar_rev_id=ct_rev_id' ] ] ] |
| 173 | ); |
| 174 | try { |
| 175 | $this->addWhereFld( 'ct_tag_id', $this->changeTagDefStore->getId( $params['tag'] ) ); |
| 176 | } catch ( NameTableAccessException ) { |
| 177 | // Return nothing. |
| 178 | $this->addWhere( '1=0' ); |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | // This means stricter restrictions |
| 183 | if ( $fld_content ) { |
| 184 | $this->checkUserRightsAny( [ 'deletedtext', 'undelete' ] ); |
| 185 | } |
| 186 | // Check limits |
| 187 | $userMax = $fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1; |
| 188 | $botMax = $fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2; |
| 189 | |
| 190 | $limit = $params['limit']; |
| 191 | |
| 192 | if ( $limit == 'max' ) { |
| 193 | $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax; |
| 194 | $this->getResult()->addParsedLimit( $this->getModuleName(), $limit ); |
| 195 | } |
| 196 | |
| 197 | $limit = $this->getMain()->getParamValidator()->validateValue( |
| 198 | $this, 'limit', $limit, [ |
| 199 | ParamValidator::PARAM_TYPE => 'limit', |
| 200 | IntegerDef::PARAM_MIN => 1, |
| 201 | IntegerDef::PARAM_MAX => $userMax, |
| 202 | IntegerDef::PARAM_MAX2 => $botMax, |
| 203 | IntegerDef::PARAM_IGNORE_RANGE => true, |
| 204 | ] |
| 205 | ); |
| 206 | |
| 207 | if ( $fld_token ) { |
| 208 | // Undelete tokens are identical for all pages, so we cache one here |
| 209 | $token = $user->getEditToken( '', $this->getMain()->getRequest() ); |
| 210 | } |
| 211 | |
| 212 | $dir = $params['dir']; |
| 213 | |
| 214 | // We need a custom WHERE clause that matches all titles. |
| 215 | if ( $mode == 'revs' ) { |
| 216 | $lb = $this->linkBatchFactory->newLinkBatch( $titles ); |
| 217 | $where = $lb->constructSet( 'ar', $db ); |
| 218 | $this->addWhere( $where ); |
| 219 | } elseif ( $mode == 'all' ) { |
| 220 | $this->addWhereFld( 'ar_namespace', $params['namespace'] ); |
| 221 | |
| 222 | $from = $params['from'] === null |
| 223 | ? null |
| 224 | : $this->titlePartToKey( $params['from'], $params['namespace'] ); |
| 225 | $to = $params['to'] === null |
| 226 | ? null |
| 227 | : $this->titlePartToKey( $params['to'], $params['namespace'] ); |
| 228 | $this->addWhereRange( 'ar_title', $dir, $from, $to ); |
| 229 | |
| 230 | if ( isset( $params['prefix'] ) ) { |
| 231 | $this->addWhere( |
| 232 | $db->expr( |
| 233 | 'ar_title', |
| 234 | IExpression::LIKE, |
| 235 | new LikeValue( |
| 236 | $this->titlePartToKey( $params['prefix'], $params['namespace'] ), |
| 237 | $db->anyString() |
| 238 | ) |
| 239 | ) |
| 240 | ); |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | if ( $params['user'] !== null ) { |
| 245 | // We already join on actor due to getArchiveQueryInfo() |
| 246 | $this->addWhereFld( 'actor_name', $params['user'] ); |
| 247 | } elseif ( $params['excludeuser'] !== null ) { |
| 248 | $this->addWhere( $db->expr( 'actor_name', '!=', $params['excludeuser'] ) ); |
| 249 | } |
| 250 | |
| 251 | if ( $params['user'] !== null || $params['excludeuser'] !== null ) { |
| 252 | // Paranoia: avoid brute force searches (T19342) |
| 253 | // (shouldn't be able to get here without 'deletedhistory', but |
| 254 | // check it again just in case) |
| 255 | if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) { |
| 256 | $bitmask = RevisionRecord::DELETED_USER; |
| 257 | } elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) { |
| 258 | $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED; |
| 259 | } else { |
| 260 | $bitmask = 0; |
| 261 | } |
| 262 | if ( $bitmask ) { |
| 263 | $this->addWhere( $db->bitAnd( 'ar_deleted', $bitmask ) . " != $bitmask" ); |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | if ( $params['continue'] !== null ) { |
| 268 | $op = ( $dir == 'newer' ? '>=' : '<=' ); |
| 269 | if ( $mode == 'all' || $mode == 'revs' ) { |
| 270 | $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'int', 'string', 'timestamp', 'int' ] ); |
| 271 | $this->addWhere( $db->buildComparison( $op, [ |
| 272 | 'ar_namespace' => $cont[0], |
| 273 | 'ar_title' => $cont[1], |
| 274 | 'ar_timestamp' => $db->timestamp( $cont[2] ), |
| 275 | 'ar_id' => $cont[3], |
| 276 | ] ) ); |
| 277 | } else { |
| 278 | $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'timestamp', 'int' ] ); |
| 279 | $this->addWhere( $db->buildComparison( $op, [ |
| 280 | 'ar_timestamp' => $db->timestamp( $cont[0] ), |
| 281 | 'ar_id' => $cont[1], |
| 282 | ] ) ); |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | $this->addOption( 'LIMIT', $limit + 1 ); |
| 287 | if ( $mode == 'all' ) { |
| 288 | if ( $params['unique'] ) { |
| 289 | // @todo Does this work on non-MySQL? |
| 290 | $this->addOption( 'GROUP BY', 'ar_title' ); |
| 291 | } else { |
| 292 | $sort = ( $dir == 'newer' ? '' : ' DESC' ); |
| 293 | $this->addOption( 'ORDER BY', [ |
| 294 | 'ar_title' . $sort, |
| 295 | 'ar_timestamp' . $sort, |
| 296 | 'ar_id' . $sort, |
| 297 | ] ); |
| 298 | } |
| 299 | } else { |
| 300 | if ( $mode == 'revs' ) { |
| 301 | // Sort by ns and title in the same order as timestamp for efficiency |
| 302 | $this->addWhereRange( 'ar_namespace', $dir, null, null ); |
| 303 | $this->addWhereRange( 'ar_title', $dir, null, null ); |
| 304 | } |
| 305 | $this->addTimestampWhereRange( 'ar_timestamp', $dir, $params['start'], $params['end'] ); |
| 306 | // Include in ORDER BY for uniqueness |
| 307 | $this->addWhereRange( 'ar_id', $dir, null, null ); |
| 308 | } |
| 309 | $res = $this->select( __METHOD__ ); |
| 310 | |
| 311 | $formattedComments = []; |
| 312 | if ( $fld_parsedcomment ) { |
| 313 | $formattedComments = $this->commentFormatter->formatItems( |
| 314 | $this->commentFormatter->rows( $res ) |
| 315 | ->indexField( 'ar_id' ) |
| 316 | ->commentKey( 'ar_comment' ) |
| 317 | ->namespaceField( 'ar_namespace' ) |
| 318 | ->titleField( 'ar_title' ) |
| 319 | ); |
| 320 | } |
| 321 | |
| 322 | $pageMap = []; // Maps ns&title to (fake) pageid |
| 323 | $count = 0; |
| 324 | $newPageID = 0; |
| 325 | foreach ( $res as $row ) { |
| 326 | if ( ++$count > $limit ) { |
| 327 | // We've had enough |
| 328 | if ( $mode == 'all' || $mode == 'revs' ) { |
| 329 | $this->setContinueEnumParameter( 'continue', |
| 330 | "$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id" |
| 331 | ); |
| 332 | } else { |
| 333 | $this->setContinueEnumParameter( 'continue', "$row->ar_timestamp|$row->ar_id" ); |
| 334 | } |
| 335 | break; |
| 336 | } |
| 337 | |
| 338 | $rev = []; |
| 339 | $anyHidden = false; |
| 340 | |
| 341 | $rev['timestamp'] = wfTimestamp( TS::ISO_8601, $row->ar_timestamp ); |
| 342 | if ( $fld_revid ) { |
| 343 | $rev['revid'] = (int)$row->ar_rev_id; |
| 344 | } |
| 345 | if ( $fld_parentid && $row->ar_parent_id !== null ) { |
| 346 | $rev['parentid'] = (int)$row->ar_parent_id; |
| 347 | } |
| 348 | if ( $fld_user || $fld_userid ) { |
| 349 | if ( $row->ar_deleted & RevisionRecord::DELETED_USER ) { |
| 350 | $rev['userhidden'] = true; |
| 351 | $anyHidden = true; |
| 352 | } |
| 353 | if ( RevisionRecord::userCanBitfield( |
| 354 | $row->ar_deleted, |
| 355 | RevisionRecord::DELETED_USER, |
| 356 | $user |
| 357 | ) ) { |
| 358 | if ( $fld_user ) { |
| 359 | $rev['user'] = $row->ar_user_text; |
| 360 | } |
| 361 | if ( $fld_userid ) { |
| 362 | $rev['userid'] = (int)$row->ar_user; |
| 363 | } |
| 364 | } |
| 365 | } |
| 366 | |
| 367 | if ( $fld_comment || $fld_parsedcomment ) { |
| 368 | if ( $row->ar_deleted & RevisionRecord::DELETED_COMMENT ) { |
| 369 | $rev['commenthidden'] = true; |
| 370 | $anyHidden = true; |
| 371 | } |
| 372 | if ( RevisionRecord::userCanBitfield( |
| 373 | $row->ar_deleted, |
| 374 | RevisionRecord::DELETED_COMMENT, |
| 375 | $user |
| 376 | ) ) { |
| 377 | $comment = $this->commentStore->getComment( 'ar_comment', $row )->text; |
| 378 | if ( $fld_comment ) { |
| 379 | $rev['comment'] = $comment; |
| 380 | } |
| 381 | if ( $fld_parsedcomment ) { |
| 382 | $rev['parsedcomment'] = $formattedComments[$row->ar_id]; |
| 383 | } |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | if ( $fld_minor ) { |
| 388 | $rev['minor'] = $row->ar_minor_edit == 1; |
| 389 | } |
| 390 | if ( $fld_len ) { |
| 391 | $rev['len'] = $row->ar_len; |
| 392 | } |
| 393 | if ( $fld_sha1 ) { |
| 394 | if ( $row->ar_deleted & RevisionRecord::DELETED_TEXT ) { |
| 395 | $rev['sha1hidden'] = true; |
| 396 | $anyHidden = true; |
| 397 | } |
| 398 | if ( RevisionRecord::userCanBitfield( |
| 399 | $row->ar_deleted, |
| 400 | RevisionRecord::DELETED_TEXT, |
| 401 | $user |
| 402 | ) ) { |
| 403 | if ( $row->ar_slot_pairs !== null ) { |
| 404 | $combinedBase36 = ''; |
| 405 | if ( $row->ar_slot_pairs !== '' ) { |
| 406 | $items = explode( ',', $row->ar_slot_pairs ); |
| 407 | $slotHashes = []; |
| 408 | foreach ( $items as $item ) { |
| 409 | $parts = explode( ':', $item ); |
| 410 | $slotHashes[$parts[0]] = $parts[1]; |
| 411 | } |
| 412 | ksort( $slotHashes ); |
| 413 | |
| 414 | $accu = null; |
| 415 | foreach ( $slotHashes as $slotHash ) { |
| 416 | $accu = $accu === null |
| 417 | ? $slotHash |
| 418 | : SlotRecord::base36Sha1( $accu . $slotHash ); |
| 419 | } |
| 420 | $combinedBase36 = $accu ?? SlotRecord::base36Sha1( '' ); |
| 421 | } |
| 422 | |
| 423 | $rev['sha1'] = $combinedBase36 !== '' |
| 424 | ? \Wikimedia\base_convert( $combinedBase36, 36, 16, 40 ) |
| 425 | : ''; |
| 426 | } |
| 427 | } |
| 428 | } |
| 429 | if ( $fld_content ) { |
| 430 | if ( $row->ar_deleted & RevisionRecord::DELETED_TEXT ) { |
| 431 | $rev['texthidden'] = true; |
| 432 | $anyHidden = true; |
| 433 | } |
| 434 | if ( RevisionRecord::userCanBitfield( |
| 435 | $row->ar_deleted, |
| 436 | RevisionRecord::DELETED_TEXT, |
| 437 | $user |
| 438 | ) ) { |
| 439 | ApiResult::setContentValue( $rev, 'text', |
| 440 | $this->revisionStore->newRevisionFromArchiveRow( $row ) |
| 441 | ->getContent( SlotRecord::MAIN )->serialize() ); |
| 442 | } |
| 443 | } |
| 444 | |
| 445 | if ( $fld_tags ) { |
| 446 | if ( $row->ts_tags ) { |
| 447 | $tags = explode( ',', $row->ts_tags ); |
| 448 | ApiResult::setIndexedTagName( $tags, 'tag' ); |
| 449 | $rev['tags'] = $tags; |
| 450 | } else { |
| 451 | $rev['tags'] = []; |
| 452 | } |
| 453 | } |
| 454 | |
| 455 | if ( $anyHidden && ( $row->ar_deleted & RevisionRecord::DELETED_RESTRICTED ) ) { |
| 456 | $rev['suppressed'] = true; |
| 457 | } |
| 458 | |
| 459 | if ( !isset( $pageMap[$row->ar_namespace][$row->ar_title] ) ) { |
| 460 | $pageID = $newPageID++; |
| 461 | $pageMap[$row->ar_namespace][$row->ar_title] = $pageID; |
| 462 | $a = [ 'revisions' => [ $rev ] ]; |
| 463 | ApiResult::setIndexedTagName( $a['revisions'], 'rev' ); |
| 464 | $title = Title::makeTitle( $row->ar_namespace, $row->ar_title ); |
| 465 | ApiQueryBase::addTitleInfo( $a, $title ); |
| 466 | if ( $fld_token ) { |
| 467 | // @phan-suppress-next-line PhanPossiblyUndeclaredVariable token is set when used |
| 468 | $a['token'] = $token; |
| 469 | } |
| 470 | $fit = $result->addValue( [ 'query', $this->getModuleName() ], $pageID, $a ); |
| 471 | } else { |
| 472 | $pageID = $pageMap[$row->ar_namespace][$row->ar_title]; |
| 473 | $fit = $result->addValue( |
| 474 | [ 'query', $this->getModuleName(), $pageID, 'revisions' ], |
| 475 | null, $rev ); |
| 476 | } |
| 477 | if ( !$fit ) { |
| 478 | if ( $mode == 'all' || $mode == 'revs' ) { |
| 479 | $this->setContinueEnumParameter( 'continue', |
| 480 | "$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id" |
| 481 | ); |
| 482 | } else { |
| 483 | $this->setContinueEnumParameter( 'continue', "$row->ar_timestamp|$row->ar_id" ); |
| 484 | } |
| 485 | break; |
| 486 | } |
| 487 | } |
| 488 | $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'page' ); |
| 489 | } |
| 490 | |
| 491 | /** @inheritDoc */ |
| 492 | public function isDeprecated() { |
| 493 | return true; |
| 494 | } |
| 495 | |
| 496 | /** @inheritDoc */ |
| 497 | public function getAllowedParams() { |
| 498 | $smallLimit = $this->getMain()->canApiHighLimits() ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_SML1; |
| 499 | return [ |
| 500 | 'start' => [ |
| 501 | ParamValidator::PARAM_TYPE => 'timestamp', |
| 502 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 1, 2 ] ], |
| 503 | ], |
| 504 | 'end' => [ |
| 505 | ParamValidator::PARAM_TYPE => 'timestamp', |
| 506 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 1, 2 ] ], |
| 507 | ], |
| 508 | 'dir' => [ |
| 509 | ParamValidator::PARAM_TYPE => [ |
| 510 | 'newer', |
| 511 | 'older' |
| 512 | ], |
| 513 | ParamValidator::PARAM_DEFAULT => 'older', |
| 514 | ApiBase::PARAM_HELP_MSG => 'api-help-param-direction', |
| 515 | ApiBase::PARAM_HELP_MSG_PER_VALUE => [ |
| 516 | 'newer' => 'api-help-paramvalue-direction-newer', |
| 517 | 'older' => 'api-help-paramvalue-direction-older', |
| 518 | ], |
| 519 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 1, 3 ] ], |
| 520 | ], |
| 521 | 'from' => [ |
| 522 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ], |
| 523 | ], |
| 524 | 'to' => [ |
| 525 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ], |
| 526 | ], |
| 527 | 'prefix' => [ |
| 528 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ], |
| 529 | ], |
| 530 | 'unique' => [ |
| 531 | ParamValidator::PARAM_DEFAULT => false, |
| 532 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ], |
| 533 | ], |
| 534 | 'namespace' => [ |
| 535 | ParamValidator::PARAM_TYPE => 'namespace', |
| 536 | ParamValidator::PARAM_DEFAULT => NS_MAIN, |
| 537 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'modes', 3 ] ], |
| 538 | ], |
| 539 | 'tag' => null, |
| 540 | 'user' => [ |
| 541 | ParamValidator::PARAM_TYPE => 'user', |
| 542 | UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'temp', 'id', 'interwiki' ], |
| 543 | ], |
| 544 | 'excludeuser' => [ |
| 545 | ParamValidator::PARAM_TYPE => 'user', |
| 546 | UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'temp', 'id', 'interwiki' ], |
| 547 | ], |
| 548 | 'prop' => [ |
| 549 | ParamValidator::PARAM_DEFAULT => 'user|comment', |
| 550 | ParamValidator::PARAM_TYPE => [ |
| 551 | 'revid', |
| 552 | 'parentid', |
| 553 | 'user', |
| 554 | 'userid', |
| 555 | 'comment', |
| 556 | 'parsedcomment', |
| 557 | 'minor', |
| 558 | 'len', |
| 559 | 'sha1', |
| 560 | 'content', |
| 561 | 'token', |
| 562 | 'tags' |
| 563 | ], |
| 564 | ParamValidator::PARAM_ISMULTI => true, |
| 565 | ApiBase::PARAM_HELP_MSG_PER_VALUE => [ |
| 566 | 'content' => [ 'apihelp-query+deletedrevs-paramvalue-prop-content', $smallLimit ], |
| 567 | ], |
| 568 | EnumDef::PARAM_DEPRECATED_VALUES => [ |
| 569 | 'token' => true, |
| 570 | ], |
| 571 | ], |
| 572 | 'limit' => [ |
| 573 | ParamValidator::PARAM_DEFAULT => 10, |
| 574 | ParamValidator::PARAM_TYPE => 'limit', |
| 575 | IntegerDef::PARAM_MIN => 1, |
| 576 | IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1, |
| 577 | IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2, |
| 578 | ApiBase::PARAM_HELP_MSG => [ 'apihelp-query+deletedrevs-param-limit', $smallLimit ], |
| 579 | ], |
| 580 | 'continue' => [ |
| 581 | ApiBase::PARAM_HELP_MSG => 'api-help-param-continue', |
| 582 | ], |
| 583 | ]; |
| 584 | } |
| 585 | |
| 586 | /** @inheritDoc */ |
| 587 | protected function getExamplesMessages() { |
| 588 | $title = Title::newMainPage(); |
| 589 | $talkTitle = $title->getTalkPageIfDefined(); |
| 590 | $examples = []; |
| 591 | |
| 592 | if ( $talkTitle ) { |
| 593 | $title = rawurlencode( $title->getPrefixedText() ); |
| 594 | $talkTitle = rawurlencode( $talkTitle->getPrefixedText() ); |
| 595 | $examples = [ |
| 596 | "action=query&list=deletedrevs&titles={$title}|{$talkTitle}&" . |
| 597 | 'drprop=user|comment|content' |
| 598 | => 'apihelp-query+deletedrevs-example-mode1', |
| 599 | ]; |
| 600 | } |
| 601 | |
| 602 | return array_merge( $examples, [ |
| 603 | 'action=query&list=deletedrevs&druser=Bob&drlimit=50' |
| 604 | => 'apihelp-query+deletedrevs-example-mode2', |
| 605 | 'action=query&list=deletedrevs&drdir=newer&drlimit=50' |
| 606 | => 'apihelp-query+deletedrevs-example-mode3-main', |
| 607 | 'action=query&list=deletedrevs&drdir=newer&drlimit=50&drnamespace=1&drunique=' |
| 608 | => 'apihelp-query+deletedrevs-example-mode3-talk', |
| 609 | ] ); |
| 610 | } |
| 611 | |
| 612 | /** @inheritDoc */ |
| 613 | public function getHelpUrls() { |
| 614 | return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Deletedrevs'; |
| 615 | } |
| 616 | } |
| 617 | |
| 618 | /** @deprecated class alias since 1.43 */ |
| 619 | class_alias( ApiQueryDeletedrevs::class, 'ApiQueryDeletedrevs' ); |