Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
51.14% |
157 / 307 |
|
40.00% |
2 / 5 |
CRAP | |
0.00% |
0 / 1 |
ApiQueryRevisions | |
51.14% |
157 / 307 |
|
40.00% |
2 / 5 |
712.74 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
1 | |||
run | |
41.47% |
90 / 217 |
|
0.00% |
0 / 1 |
1052.26 | |||
getAllowedParams | |
100.00% |
49 / 49 |
|
100.00% |
1 / 1 |
1 | |||
getExamplesMessages | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
2 | |||
getHelpUrls | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com" |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | */ |
22 | |
23 | use MediaWiki\CommentFormatter\CommentFormatter; |
24 | use MediaWiki\Content\IContentHandlerFactory; |
25 | use MediaWiki\Content\Renderer\ContentRenderer; |
26 | use MediaWiki\Content\Transform\ContentTransformer; |
27 | use MediaWiki\Page\PageIdentity; |
28 | use MediaWiki\ParamValidator\TypeDef\UserDef; |
29 | use MediaWiki\Revision\RevisionRecord; |
30 | use MediaWiki\Revision\RevisionStore; |
31 | use MediaWiki\Revision\SlotRoleRegistry; |
32 | use MediaWiki\Status\Status; |
33 | use MediaWiki\Storage\NameTableAccessException; |
34 | use MediaWiki\Storage\NameTableStore; |
35 | use MediaWiki\Title\Title; |
36 | use MediaWiki\Title\TitleFormatter; |
37 | use MediaWiki\User\ActorMigration; |
38 | use MediaWiki\User\TempUser\TempUserCreator; |
39 | use MediaWiki\User\UserFactory; |
40 | use Wikimedia\ParamValidator\ParamValidator; |
41 | |
42 | /** |
43 | * A query action to enumerate revisions of a given page, or show top revisions |
44 | * of multiple pages. Various pieces of information may be shown - flags, |
45 | * comments, and the actual wiki markup of the rev. In the enumeration mode, |
46 | * ranges of revisions may be requested and filtered. |
47 | * |
48 | * @ingroup API |
49 | */ |
50 | class ApiQueryRevisions extends ApiQueryRevisionsBase { |
51 | |
52 | private RevisionStore $revisionStore; |
53 | private NameTableStore $changeTagDefStore; |
54 | private ActorMigration $actorMigration; |
55 | private TitleFormatter $titleFormatter; |
56 | |
57 | /** |
58 | * @param ApiQuery $query |
59 | * @param string $moduleName |
60 | * @param RevisionStore $revisionStore |
61 | * @param IContentHandlerFactory $contentHandlerFactory |
62 | * @param ParserFactory $parserFactory |
63 | * @param SlotRoleRegistry $slotRoleRegistry |
64 | * @param NameTableStore $changeTagDefStore |
65 | * @param ActorMigration $actorMigration |
66 | * @param ContentRenderer $contentRenderer |
67 | * @param ContentTransformer $contentTransformer |
68 | * @param CommentFormatter $commentFormatter |
69 | * @param TempUserCreator $tempUserCreator |
70 | * @param UserFactory $userFactory |
71 | * @param TitleFormatter $titleFormatter |
72 | */ |
73 | public function __construct( |
74 | ApiQuery $query, |
75 | $moduleName, |
76 | RevisionStore $revisionStore, |
77 | IContentHandlerFactory $contentHandlerFactory, |
78 | ParserFactory $parserFactory, |
79 | SlotRoleRegistry $slotRoleRegistry, |
80 | NameTableStore $changeTagDefStore, |
81 | ActorMigration $actorMigration, |
82 | ContentRenderer $contentRenderer, |
83 | ContentTransformer $contentTransformer, |
84 | CommentFormatter $commentFormatter, |
85 | TempUserCreator $tempUserCreator, |
86 | UserFactory $userFactory, |
87 | TitleFormatter $titleFormatter |
88 | ) { |
89 | parent::__construct( |
90 | $query, |
91 | $moduleName, |
92 | 'rv', |
93 | $revisionStore, |
94 | $contentHandlerFactory, |
95 | $parserFactory, |
96 | $slotRoleRegistry, |
97 | $contentRenderer, |
98 | $contentTransformer, |
99 | $commentFormatter, |
100 | $tempUserCreator, |
101 | $userFactory |
102 | ); |
103 | $this->revisionStore = $revisionStore; |
104 | $this->changeTagDefStore = $changeTagDefStore; |
105 | $this->actorMigration = $actorMigration; |
106 | $this->titleFormatter = $titleFormatter; |
107 | } |
108 | |
109 | protected function run( ApiPageSet $resultPageSet = null ) { |
110 | $params = $this->extractRequestParams( false ); |
111 | |
112 | // If any of those parameters are used, work in 'enumeration' mode. |
113 | // Enum mode can only be used when exactly one page is provided. |
114 | // Enumerating revisions on multiple pages make it extremely |
115 | // difficult to manage continuations and require additional SQL indexes |
116 | $enumRevMode = ( $params['user'] !== null || $params['excludeuser'] !== null || |
117 | $params['limit'] !== null || $params['startid'] !== null || |
118 | $params['endid'] !== null || $params['dir'] === 'newer' || |
119 | $params['start'] !== null || $params['end'] !== null ); |
120 | |
121 | $pageSet = $this->getPageSet(); |
122 | $pageCount = $pageSet->getGoodTitleCount(); |
123 | $revCount = $pageSet->getRevisionCount(); |
124 | |
125 | // Optimization -- nothing to do |
126 | if ( $revCount === 0 && $pageCount === 0 ) { |
127 | // Nothing to do |
128 | return; |
129 | } |
130 | if ( $revCount > 0 && count( $pageSet->getLiveRevisionIDs() ) === 0 ) { |
131 | // We're in revisions mode but all given revisions are deleted |
132 | return; |
133 | } |
134 | |
135 | if ( $revCount > 0 && $enumRevMode ) { |
136 | $this->dieWithError( |
137 | [ 'apierror-revisions-norevids', $this->getModulePrefix() ], 'invalidparammix' |
138 | ); |
139 | } |
140 | |
141 | if ( $pageCount > 1 && $enumRevMode ) { |
142 | $this->dieWithError( |
143 | [ 'apierror-revisions-singlepage', $this->getModulePrefix() ], 'invalidparammix' |
144 | ); |
145 | } |
146 | |
147 | // In non-enum mode, rvlimit can't be directly used. Use the maximum |
148 | // allowed value. |
149 | if ( !$enumRevMode ) { |
150 | $this->setParsedLimit = false; |
151 | $params['limit'] = 'max'; |
152 | } |
153 | |
154 | $db = $this->getDB(); |
155 | |
156 | $idField = 'rev_id'; |
157 | $tsField = 'rev_timestamp'; |
158 | $pageField = 'rev_page'; |
159 | |
160 | $ignoreIndex = [ |
161 | // T224017: `rev_timestamp` is never the correct index to use for this module, but |
162 | // MariaDB sometimes insists on trying to use it anyway. Tell it not to. |
163 | // Last checked with MariaDB 10.4.13 |
164 | 'revision' => 'rev_timestamp', |
165 | ]; |
166 | $useIndex = []; |
167 | if ( $resultPageSet === null ) { |
168 | $this->parseParameters( $params ); |
169 | $queryBuilder = $this->revisionStore->newSelectQueryBuilder( $db ) |
170 | ->joinComment() |
171 | ->joinPage(); |
172 | if ( $this->fld_user ) { |
173 | $queryBuilder->joinUser(); |
174 | } |
175 | $this->getQueryBuilder()->merge( $queryBuilder ); |
176 | } else { |
177 | $this->limit = $this->getParameter( 'limit' ) ?: 10; |
178 | // Always join 'page' so orphaned revisions are filtered out |
179 | $this->addTables( [ 'revision', 'page' ] ); |
180 | $this->addJoinConds( |
181 | [ 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ] ] |
182 | ); |
183 | $this->addFields( [ |
184 | 'rev_id' => $idField, 'rev_timestamp' => $tsField, 'rev_page' => $pageField |
185 | ] ); |
186 | } |
187 | |
188 | if ( $this->fld_tags ) { |
189 | $this->addFields( [ 'ts_tags' => ChangeTags::makeTagSummarySubquery( 'revision' ) ] ); |
190 | } |
191 | |
192 | if ( $params['tag'] !== null ) { |
193 | $this->addTables( 'change_tag' ); |
194 | $this->addJoinConds( |
195 | [ 'change_tag' => [ 'JOIN', [ 'rev_id=ct_rev_id' ] ] ] |
196 | ); |
197 | try { |
198 | $this->addWhereFld( 'ct_tag_id', $this->changeTagDefStore->getId( $params['tag'] ) ); |
199 | } catch ( NameTableAccessException $exception ) { |
200 | // Return nothing. |
201 | $this->addWhere( '1=0' ); |
202 | } |
203 | } |
204 | |
205 | if ( $resultPageSet === null && $this->fetchContent ) { |
206 | // For each page we will request, the user must have read rights for that page |
207 | $status = Status::newGood(); |
208 | |
209 | /** @var PageIdentity $pageIdentity */ |
210 | foreach ( $pageSet->getGoodPages() as $pageIdentity ) { |
211 | if ( !$this->getAuthority()->authorizeRead( 'read', $pageIdentity ) ) { |
212 | $status->fatal( ApiMessage::create( |
213 | [ |
214 | 'apierror-cannotviewtitle', |
215 | wfEscapeWikiText( $this->titleFormatter->getPrefixedText( $pageIdentity ) ), |
216 | ], |
217 | 'accessdenied' |
218 | ) ); |
219 | } |
220 | } |
221 | if ( !$status->isGood() ) { |
222 | $this->dieStatus( $status ); |
223 | } |
224 | } |
225 | |
226 | if ( $enumRevMode ) { |
227 | // Indexes targeted: |
228 | // page_timestamp if we don't have rvuser |
229 | // page_actor_timestamp (on revision_actor_temp) if we have rvuser in READ_NEW mode |
230 | // page_user_timestamp if we have a logged-in rvuser |
231 | // page_timestamp or usertext_timestamp if we have an IP rvuser |
232 | |
233 | // This is mostly to prevent parameter errors (and optimize SQL?) |
234 | $this->requireMaxOneParameter( $params, 'startid', 'start' ); |
235 | $this->requireMaxOneParameter( $params, 'endid', 'end' ); |
236 | $this->requireMaxOneParameter( $params, 'user', 'excludeuser' ); |
237 | |
238 | if ( $params['continue'] !== null ) { |
239 | $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'timestamp', 'int' ] ); |
240 | $op = ( $params['dir'] === 'newer' ? '>=' : '<=' ); |
241 | $continueTimestamp = $db->timestamp( $cont[0] ); |
242 | $continueId = (int)$cont[1]; |
243 | $this->addWhere( $db->buildComparison( $op, [ |
244 | $tsField => $continueTimestamp, |
245 | $idField => $continueId, |
246 | ] ) ); |
247 | } |
248 | |
249 | // Convert startid/endid to timestamps (T163532) |
250 | $revids = []; |
251 | if ( $params['startid'] !== null ) { |
252 | $revids[] = (int)$params['startid']; |
253 | } |
254 | if ( $params['endid'] !== null ) { |
255 | $revids[] = (int)$params['endid']; |
256 | } |
257 | if ( $revids ) { |
258 | $db = $this->getDB(); |
259 | $uqb = $db->newUnionQueryBuilder(); |
260 | $uqb->add( |
261 | $db->newSelectQueryBuilder() |
262 | ->select( [ 'id' => 'rev_id', 'ts' => 'rev_timestamp' ] ) |
263 | ->from( 'revision' ) |
264 | ->where( [ 'rev_id' => $revids ] ) |
265 | ); |
266 | $uqb->add( |
267 | $db->newSelectQueryBuilder() |
268 | ->select( [ 'id' => 'ar_rev_id', 'ts' => 'ar_timestamp' ] ) |
269 | ->from( 'archive' ) |
270 | ->where( [ 'ar_rev_id' => $revids ] ) |
271 | ); |
272 | $res = $uqb->caller( __METHOD__ )->fetchResultSet(); |
273 | foreach ( $res as $row ) { |
274 | if ( (int)$row->id === (int)$params['startid'] ) { |
275 | $params['start'] = $row->ts; |
276 | } |
277 | if ( (int)$row->id === (int)$params['endid'] ) { |
278 | $params['end'] = $row->ts; |
279 | } |
280 | } |
281 | // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset False positive |
282 | if ( $params['startid'] !== null && $params['start'] === null ) { |
283 | $p = $this->encodeParamName( 'startid' ); |
284 | $this->dieWithError( [ 'apierror-revisions-badid', $p ], "badid_$p" ); |
285 | } |
286 | // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset False positive |
287 | if ( $params['endid'] !== null && $params['end'] === null ) { |
288 | $p = $this->encodeParamName( 'endid' ); |
289 | $this->dieWithError( [ 'apierror-revisions-badid', $p ], "badid_$p" ); |
290 | } |
291 | |
292 | // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset False positive |
293 | if ( $params['start'] !== null ) { |
294 | $op = ( $params['dir'] === 'newer' ? '>=' : '<=' ); |
295 | // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset False positive |
296 | $ts = $db->timestampOrNull( $params['start'] ); |
297 | if ( $params['startid'] !== null ) { |
298 | $this->addWhere( $db->buildComparison( $op, [ |
299 | $tsField => $ts, |
300 | $idField => (int)$params['startid'], |
301 | ] ) ); |
302 | } else { |
303 | $this->addWhere( $db->buildComparison( $op, [ $tsField => $ts ] ) ); |
304 | } |
305 | } |
306 | // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset False positive |
307 | if ( $params['end'] !== null ) { |
308 | $op = ( $params['dir'] === 'newer' ? '<=' : '>=' ); // Yes, opposite of the above |
309 | // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset False positive |
310 | $ts = $db->timestampOrNull( $params['end'] ); |
311 | if ( $params['endid'] !== null ) { |
312 | $this->addWhere( $db->buildComparison( $op, [ |
313 | $tsField => $ts, |
314 | $idField => (int)$params['endid'], |
315 | ] ) ); |
316 | } else { |
317 | $this->addWhere( $db->buildComparison( $op, [ $tsField => $ts ] ) ); |
318 | } |
319 | } |
320 | } else { |
321 | $this->addTimestampWhereRange( $tsField, $params['dir'], |
322 | $params['start'], $params['end'] ); |
323 | } |
324 | |
325 | $sort = ( $params['dir'] === 'newer' ? '' : 'DESC' ); |
326 | $this->addOption( 'ORDER BY', [ "rev_timestamp $sort", "rev_id $sort" ] ); |
327 | |
328 | // There is only one ID, use it |
329 | $ids = array_keys( $pageSet->getGoodPages() ); |
330 | $this->addWhereFld( $pageField, reset( $ids ) ); |
331 | |
332 | if ( $params['user'] !== null ) { |
333 | $actorQuery = $this->actorMigration->getWhere( $db, 'rev_user', $params['user'] ); |
334 | $this->addTables( $actorQuery['tables'] ); |
335 | $this->addJoinConds( $actorQuery['joins'] ); |
336 | $this->addWhere( $actorQuery['conds'] ); |
337 | } elseif ( $params['excludeuser'] !== null ) { |
338 | $actorQuery = $this->actorMigration->getWhere( $db, 'rev_user', $params['excludeuser'] ); |
339 | $this->addTables( $actorQuery['tables'] ); |
340 | $this->addJoinConds( $actorQuery['joins'] ); |
341 | $this->addWhere( 'NOT(' . $actorQuery['conds'] . ')' ); |
342 | } else { |
343 | // T258480: MariaDB ends up using rev_page_actor_timestamp in some cases here. |
344 | // Last checked with MariaDB 10.4.13 |
345 | // Unless we are filtering by user (see above), we always want to use the |
346 | // "history" index on the revision table, namely page_timestamp. |
347 | $useIndex['revision'] = 'rev_page_timestamp'; |
348 | } |
349 | |
350 | if ( $params['user'] !== null || $params['excludeuser'] !== null ) { |
351 | // Paranoia: avoid brute force searches (T19342) |
352 | if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) { |
353 | $bitmask = RevisionRecord::DELETED_USER; |
354 | } elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) { |
355 | $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED; |
356 | } else { |
357 | $bitmask = 0; |
358 | } |
359 | if ( $bitmask ) { |
360 | $this->addWhere( $db->bitAnd( 'rev_deleted', $bitmask ) . " != $bitmask" ); |
361 | } |
362 | } |
363 | } elseif ( $revCount > 0 ) { |
364 | // Always targets the PRIMARY index |
365 | |
366 | $revs = $pageSet->getLiveRevisionIDs(); |
367 | |
368 | // Get all revision IDs |
369 | $this->addWhereFld( 'rev_id', array_keys( $revs ) ); |
370 | |
371 | if ( $params['continue'] !== null ) { |
372 | $this->addWhere( $db->buildComparison( '>=', [ |
373 | 'rev_id' => (int)$params['continue'] |
374 | ] ) ); |
375 | } |
376 | $this->addOption( 'ORDER BY', 'rev_id' ); |
377 | } elseif ( $pageCount > 0 ) { |
378 | // Always targets the rev_page_id index |
379 | |
380 | $pageids = array_keys( $pageSet->getGoodPages() ); |
381 | |
382 | // When working in multi-page non-enumeration mode, |
383 | // limit to the latest revision only |
384 | $this->addWhere( 'page_latest=rev_id' ); |
385 | |
386 | // Get all page IDs |
387 | $this->addWhereFld( 'page_id', $pageids ); |
388 | // Every time someone relies on equality propagation, god kills a kitten :) |
389 | $this->addWhereFld( 'rev_page', $pageids ); |
390 | |
391 | if ( $params['continue'] !== null ) { |
392 | $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'int', 'int' ] ); |
393 | $this->addWhere( $db->buildComparison( '>=', [ |
394 | 'rev_page' => $cont[0], |
395 | 'rev_id' => $cont[1], |
396 | ] ) ); |
397 | } |
398 | $this->addOption( 'ORDER BY', [ |
399 | 'rev_page', |
400 | 'rev_id' |
401 | ] ); |
402 | } else { |
403 | ApiBase::dieDebug( __METHOD__, 'param validation?' ); |
404 | } |
405 | |
406 | $this->addOption( 'LIMIT', $this->limit + 1 ); |
407 | |
408 | $this->addOption( 'IGNORE INDEX', $ignoreIndex ); |
409 | |
410 | if ( $useIndex ) { |
411 | $this->addOption( 'USE INDEX', $useIndex ); |
412 | } |
413 | |
414 | $count = 0; |
415 | $generated = []; |
416 | $hookData = []; |
417 | $res = $this->select( __METHOD__, [], $hookData ); |
418 | |
419 | foreach ( $res as $row ) { |
420 | if ( ++$count > $this->limit ) { |
421 | // We've reached the one extra which shows that there are |
422 | // additional pages to be had. Stop here... |
423 | if ( $enumRevMode ) { |
424 | $this->setContinueEnumParameter( 'continue', |
425 | $row->rev_timestamp . '|' . (int)$row->rev_id ); |
426 | } elseif ( $revCount > 0 ) { |
427 | $this->setContinueEnumParameter( 'continue', (int)$row->rev_id ); |
428 | } else { |
429 | $this->setContinueEnumParameter( 'continue', (int)$row->rev_page . |
430 | '|' . (int)$row->rev_id ); |
431 | } |
432 | break; |
433 | } |
434 | |
435 | if ( $resultPageSet !== null ) { |
436 | $generated[] = $row->rev_id; |
437 | } else { |
438 | $revision = $this->revisionStore->newRevisionFromRow( $row, 0, Title::newFromRow( $row ) ); |
439 | $rev = $this->extractRevisionInfo( $revision, $row ); |
440 | $fit = $this->processRow( $row, $rev, $hookData ) && |
441 | $this->addPageSubItem( $row->rev_page, $rev, 'rev' ); |
442 | if ( !$fit ) { |
443 | if ( $enumRevMode ) { |
444 | $this->setContinueEnumParameter( 'continue', |
445 | $row->rev_timestamp . '|' . (int)$row->rev_id ); |
446 | } elseif ( $revCount > 0 ) { |
447 | $this->setContinueEnumParameter( 'continue', (int)$row->rev_id ); |
448 | } else { |
449 | $this->setContinueEnumParameter( 'continue', (int)$row->rev_page . |
450 | '|' . (int)$row->rev_id ); |
451 | } |
452 | break; |
453 | } |
454 | } |
455 | } |
456 | |
457 | if ( $resultPageSet !== null ) { |
458 | $resultPageSet->populateFromRevisionIDs( $generated ); |
459 | } |
460 | } |
461 | |
462 | public function getAllowedParams() { |
463 | $ret = parent::getAllowedParams() + [ |
464 | 'startid' => [ |
465 | ParamValidator::PARAM_TYPE => 'integer', |
466 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ], |
467 | ], |
468 | 'endid' => [ |
469 | ParamValidator::PARAM_TYPE => 'integer', |
470 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ], |
471 | ], |
472 | 'start' => [ |
473 | ParamValidator::PARAM_TYPE => 'timestamp', |
474 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ], |
475 | ], |
476 | 'end' => [ |
477 | ParamValidator::PARAM_TYPE => 'timestamp', |
478 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ], |
479 | ], |
480 | 'dir' => [ |
481 | ParamValidator::PARAM_DEFAULT => 'older', |
482 | ParamValidator::PARAM_TYPE => [ |
483 | 'newer', |
484 | 'older' |
485 | ], |
486 | ApiBase::PARAM_HELP_MSG => 'api-help-param-direction', |
487 | ApiBase::PARAM_HELP_MSG_PER_VALUE => [ |
488 | 'newer' => 'api-help-paramvalue-direction-newer', |
489 | 'older' => 'api-help-paramvalue-direction-older', |
490 | ], |
491 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ], |
492 | ], |
493 | 'user' => [ |
494 | ParamValidator::PARAM_TYPE => 'user', |
495 | UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'temp', 'id', 'interwiki' ], |
496 | UserDef::PARAM_RETURN_OBJECT => true, |
497 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ], |
498 | ], |
499 | 'excludeuser' => [ |
500 | ParamValidator::PARAM_TYPE => 'user', |
501 | UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'temp', 'id', 'interwiki' ], |
502 | UserDef::PARAM_RETURN_OBJECT => true, |
503 | ApiBase::PARAM_HELP_MSG_INFO => [ [ 'singlepageonly' ] ], |
504 | ], |
505 | 'tag' => null, |
506 | 'continue' => [ |
507 | ApiBase::PARAM_HELP_MSG => 'api-help-param-continue', |
508 | ], |
509 | ]; |
510 | |
511 | $ret['limit'][ApiBase::PARAM_HELP_MSG_INFO] = [ [ 'singlepageonly' ] ]; |
512 | |
513 | return $ret; |
514 | } |
515 | |
516 | protected function getExamplesMessages() { |
517 | $title = Title::newMainPage()->getPrefixedText(); |
518 | $mp = rawurlencode( $title ); |
519 | |
520 | return [ |
521 | "action=query&prop=revisions&titles=API|{$mp}&" . |
522 | 'rvslots=*&rvprop=timestamp|user|comment|content' |
523 | => 'apihelp-query+revisions-example-content', |
524 | "action=query&prop=revisions&titles={$mp}&rvlimit=5&" . |
525 | 'rvprop=timestamp|user|comment' |
526 | => 'apihelp-query+revisions-example-last5', |
527 | "action=query&prop=revisions&titles={$mp}&rvlimit=5&" . |
528 | 'rvprop=timestamp|user|comment&rvdir=newer' |
529 | => 'apihelp-query+revisions-example-first5', |
530 | "action=query&prop=revisions&titles={$mp}&rvlimit=5&" . |
531 | 'rvprop=timestamp|user|comment&rvdir=newer&rvstart=2006-05-01T00:00:00Z' |
532 | => 'apihelp-query+revisions-example-first5-after', |
533 | "action=query&prop=revisions&titles={$mp}&rvlimit=5&" . |
534 | 'rvprop=timestamp|user|comment&rvexcludeuser=127.0.0.1' |
535 | => 'apihelp-query+revisions-example-first5-not-localhost', |
536 | "action=query&prop=revisions&titles={$mp}&rvlimit=5&" . |
537 | 'rvprop=timestamp|user|comment&rvuser=MediaWiki%20default' |
538 | => 'apihelp-query+revisions-example-first5-user', |
539 | ]; |
540 | } |
541 | |
542 | public function getHelpUrls() { |
543 | return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Revisions'; |
544 | } |
545 | } |