Fix use of GenderCache in ApiPageSet::processTitlesArray
[lhc/web/wiklou.git] / includes / api / ApiQueryRevisionsBase.php
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\Logger\LoggerFactory;
24 use MediaWiki\Revision\RevisionAccessException;
25 use MediaWiki\Revision\RevisionRecord;
26 use MediaWiki\Revision\SlotRecord;
27 use MediaWiki\MediaWikiServices;
28
29 /**
30 * A base class for functions common to producing a list of revisions.
31 *
32 * @ingroup API
33 */
34 abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
35
36 /**
37 * @name Constants for internal use. Don't use externally.
38 * @{
39 */
40
41 // Bits to indicate the results of the revdel permission check on a revision,
42 // see self::checkRevDel()
43 const IS_DELETED = 1; // Whether the field is revision-deleted
44 const CANNOT_VIEW = 2; // Whether the user cannot view the field due to revdel
45
46 /** @} */
47
48 protected $limit, $diffto, $difftotext, $difftotextpst, $expandTemplates, $generateXML,
49 $section, $parseContent, $fetchContent, $contentFormat, $setParsedLimit = true,
50 $slotRoles = null, $needSlots;
51
52 protected $fld_ids = false, $fld_flags = false, $fld_timestamp = false,
53 $fld_size = false, $fld_slotsize = false, $fld_sha1 = false, $fld_slotsha1 = false,
54 $fld_comment = false, $fld_parsedcomment = false, $fld_user = false, $fld_userid = false,
55 $fld_content = false, $fld_tags = false, $fld_contentmodel = false, $fld_roles = false,
56 $fld_parsetree = false;
57
58 public function execute() {
59 $this->run();
60 }
61
62 public function executeGenerator( $resultPageSet ) {
63 $this->run( $resultPageSet );
64 }
65
66 /**
67 * @param ApiPageSet|null $resultPageSet
68 * @return void
69 */
70 abstract protected function run( ApiPageSet $resultPageSet = null );
71
72 /**
73 * Parse the parameters into the various instance fields.
74 *
75 * @param array $params
76 */
77 protected function parseParameters( $params ) {
78 $prop = array_flip( $params['prop'] );
79
80 $this->fld_ids = isset( $prop['ids'] );
81 $this->fld_flags = isset( $prop['flags'] );
82 $this->fld_timestamp = isset( $prop['timestamp'] );
83 $this->fld_comment = isset( $prop['comment'] );
84 $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
85 $this->fld_size = isset( $prop['size'] );
86 $this->fld_slotsize = isset( $prop['slotsize'] );
87 $this->fld_sha1 = isset( $prop['sha1'] );
88 $this->fld_slotsha1 = isset( $prop['slotsha1'] );
89 $this->fld_content = isset( $prop['content'] );
90 $this->fld_contentmodel = isset( $prop['contentmodel'] );
91 $this->fld_userid = isset( $prop['userid'] );
92 $this->fld_user = isset( $prop['user'] );
93 $this->fld_tags = isset( $prop['tags'] );
94 $this->fld_roles = isset( $prop['roles'] );
95 $this->fld_parsetree = isset( $prop['parsetree'] );
96
97 $this->slotRoles = $params['slots'];
98
99 if ( $this->slotRoles !== null ) {
100 if ( $this->fld_parsetree ) {
101 $this->dieWithError( [
102 'apierror-invalidparammix-cannotusewith',
103 $this->encodeParamName( 'prop=parsetree' ),
104 $this->encodeParamName( 'slots' ),
105 ], 'invalidparammix' );
106 }
107 foreach ( [
108 'expandtemplates', 'generatexml', 'parse', 'diffto', 'difftotext', 'difftotextpst',
109 'contentformat'
110 ] as $p ) {
111 if ( $params[$p] !== null && $params[$p] !== false ) {
112 $this->dieWithError( [
113 'apierror-invalidparammix-cannotusewith',
114 $this->encodeParamName( $p ),
115 $this->encodeParamName( 'slots' ),
116 ], 'invalidparammix' );
117 }
118 }
119 }
120
121 if ( !empty( $params['contentformat'] ) ) {
122 $this->contentFormat = $params['contentformat'];
123 }
124
125 $this->limit = $params['limit'];
126
127 if ( !is_null( $params['difftotext'] ) ) {
128 $this->difftotext = $params['difftotext'];
129 $this->difftotextpst = $params['difftotextpst'];
130 } elseif ( !is_null( $params['diffto'] ) ) {
131 if ( $params['diffto'] == 'cur' ) {
132 $params['diffto'] = 0;
133 }
134 if ( ( !ctype_digit( $params['diffto'] ) || $params['diffto'] < 0 )
135 && $params['diffto'] != 'prev' && $params['diffto'] != 'next'
136 ) {
137 $p = $this->getModulePrefix();
138 $this->dieWithError( [ 'apierror-baddiffto', $p ], 'diffto' );
139 }
140 // Check whether the revision exists and is readable,
141 // DifferenceEngine returns a rather ambiguous empty
142 // string if that's not the case
143 if ( $params['diffto'] != 0 ) {
144 $difftoRev = MediaWikiServices::getInstance()->getRevisionStore()
145 ->getRevisionById( $params['diffto'] );
146 if ( !$difftoRev ) {
147 $this->dieWithError( [ 'apierror-nosuchrevid', $params['diffto'] ] );
148 }
149 $revDel = $this->checkRevDel( $difftoRev, RevisionRecord::DELETED_TEXT );
150 if ( $revDel & self::CANNOT_VIEW ) {
151 $this->addWarning( [ 'apiwarn-difftohidden', $difftoRev->getId() ] );
152 $params['diffto'] = null;
153 }
154 }
155 $this->diffto = $params['diffto'];
156 }
157
158 $this->fetchContent = $this->fld_content || !is_null( $this->diffto )
159 || !is_null( $this->difftotext ) || $this->fld_parsetree;
160
161 $smallLimit = false;
162 if ( $this->fetchContent ) {
163 $smallLimit = true;
164 $this->expandTemplates = $params['expandtemplates'];
165 $this->generateXML = $params['generatexml'];
166 $this->parseContent = $params['parse'];
167 if ( $this->parseContent ) {
168 // Must manually initialize unset limit
169 if ( is_null( $this->limit ) ) {
170 $this->limit = 1;
171 }
172 }
173 $this->section = $params['section'] ?? false;
174 }
175
176 $userMax = $this->parseContent ? 1 : ( $smallLimit ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 );
177 $botMax = $this->parseContent ? 1 : ( $smallLimit ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 );
178 if ( $this->limit == 'max' ) {
179 $this->limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
180 if ( $this->setParsedLimit ) {
181 $this->getResult()->addParsedLimit( $this->getModuleName(), $this->limit );
182 }
183 }
184
185 if ( is_null( $this->limit ) ) {
186 $this->limit = 10;
187 }
188 $this->validateLimit( 'limit', $this->limit, 1, $userMax, $botMax );
189
190 $this->needSlots = $this->fetchContent || $this->fld_contentmodel ||
191 $this->fld_slotsize || $this->fld_slotsha1;
192 if ( $this->needSlots && $this->slotRoles === null ) {
193 $encParam = $this->encodeParamName( 'slots' );
194 $name = $this->getModuleName();
195 $parent = $this->getParent();
196 $parentParam = $parent->encodeParamName( $parent->getModuleManager()->getModuleGroup( $name ) );
197 $this->addDeprecation(
198 [ 'apiwarn-deprecation-missingparam', $encParam ],
199 "action=query&{$parentParam}={$name}&!{$encParam}"
200 );
201 }
202 }
203
204 /**
205 * Test revision deletion status
206 * @param RevisionRecord $revision Revision to check
207 * @param int $field One of the RevisionRecord::DELETED_* constants
208 * @return int Revision deletion status flags. Bitwise OR of
209 * self::IS_DELETED and self::CANNOT_VIEW, as appropriate.
210 */
211 private function checkRevDel( RevisionRecord $revision, $field ) {
212 $ret = $revision->isDeleted( $field ) ? self::IS_DELETED : 0;
213 if ( $ret ) {
214 $canSee = $revision->audienceCan( $field, RevisionRecord::FOR_THIS_USER, $this->getUser() );
215 $ret = $ret | ( $canSee ? 0 : self::CANNOT_VIEW );
216 }
217 return $ret;
218 }
219
220 /**
221 * Extract information from the RevisionRecord
222 *
223 * @since 1.32, takes a RevisionRecord instead of a Revision
224 * @param RevisionRecord $revision Revision
225 * @param object $row Should have a field 'ts_tags' if $this->fld_tags is set
226 * @return array
227 */
228 protected function extractRevisionInfo( RevisionRecord $revision, $row ) {
229 $vals = [];
230 $anyHidden = false;
231
232 if ( $this->fld_ids ) {
233 $vals['revid'] = (int)$revision->getId();
234 if ( !is_null( $revision->getParentId() ) ) {
235 $vals['parentid'] = (int)$revision->getParentId();
236 }
237 }
238
239 if ( $this->fld_flags ) {
240 $vals['minor'] = $revision->isMinor();
241 }
242
243 if ( $this->fld_user || $this->fld_userid ) {
244 $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_USER );
245 if ( ( $revDel & self::IS_DELETED ) ) {
246 $vals['userhidden'] = true;
247 $anyHidden = true;
248 }
249 if ( !( $revDel & self::CANNOT_VIEW ) ) {
250 $u = $revision->getUser( RevisionRecord::RAW );
251 if ( $this->fld_user ) {
252 $vals['user'] = $u->getName();
253 }
254 $userid = $u->getId();
255 if ( !$userid ) {
256 $vals['anon'] = true;
257 }
258
259 if ( $this->fld_userid ) {
260 $vals['userid'] = $userid;
261 }
262 }
263 }
264
265 if ( $this->fld_timestamp ) {
266 $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $revision->getTimestamp() );
267 }
268
269 if ( $this->fld_size ) {
270 try {
271 $vals['size'] = (int)$revision->getSize();
272 } catch ( RevisionAccessException $e ) {
273 // Back compat: If there's no size, return 0.
274 // @todo: Gergő says to mention T198099 as a "todo" here.
275 $vals['size'] = 0;
276 }
277 }
278
279 if ( $this->fld_sha1 ) {
280 $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_TEXT );
281 if ( ( $revDel & self::IS_DELETED ) ) {
282 $vals['sha1hidden'] = true;
283 $anyHidden = true;
284 }
285 if ( !( $revDel & self::CANNOT_VIEW ) ) {
286 try {
287 $vals['sha1'] = Wikimedia\base_convert( $revision->getSha1(), 36, 16, 40 );
288 } catch ( RevisionAccessException $e ) {
289 // Back compat: If there's no sha1, return emtpy string.
290 // @todo: Gergő says to mention T198099 as a "todo" here.
291 $vals['sha1'] = '';
292 }
293 }
294 }
295
296 try {
297 if ( $this->fld_roles ) {
298 $vals['roles'] = $revision->getSlotRoles();
299 }
300
301 if ( $this->needSlots ) {
302 $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_TEXT );
303 if ( ( $this->fld_slotsha1 || $this->fetchContent ) && ( $revDel & self::IS_DELETED ) ) {
304 $anyHidden = true;
305 }
306 $vals = array_merge( $vals, $this->extractAllSlotInfo( $revision, $revDel ) );
307 }
308 } catch ( RevisionAccessException $ex ) {
309 // This is here so T212428 doesn't spam the log.
310 // TODO: find out why T212428 happens in the first place!
311 $vals['slotsmissing'] = true;
312
313 LoggerFactory::getInstance( 'api-warning' )->error(
314 'Failed to access revision slots',
315 [ 'revision' => $revision->getId(), 'exception' => $ex, ]
316 );
317 }
318
319 if ( $this->fld_comment || $this->fld_parsedcomment ) {
320 $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_COMMENT );
321 if ( ( $revDel & self::IS_DELETED ) ) {
322 $vals['commenthidden'] = true;
323 $anyHidden = true;
324 }
325 if ( !( $revDel & self::CANNOT_VIEW ) ) {
326 $comment = $revision->getComment( RevisionRecord::RAW );
327 $comment = $comment ? $comment->text : '';
328
329 if ( $this->fld_comment ) {
330 $vals['comment'] = $comment;
331 }
332
333 if ( $this->fld_parsedcomment ) {
334 $vals['parsedcomment'] = Linker::formatComment(
335 $comment, Title::newFromLinkTarget( $revision->getPageAsLinkTarget() )
336 );
337 }
338 }
339 }
340
341 if ( $this->fld_tags ) {
342 if ( $row->ts_tags ) {
343 $tags = explode( ',', $row->ts_tags );
344 ApiResult::setIndexedTagName( $tags, 'tag' );
345 $vals['tags'] = $tags;
346 } else {
347 $vals['tags'] = [];
348 }
349 }
350
351 if ( $anyHidden && $revision->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
352 $vals['suppressed'] = true;
353 }
354
355 return $vals;
356 }
357
358 /**
359 * Extracts information about all relevant slots.
360 *
361 * @param RevisionRecord $revision
362 * @param int $revDel
363 *
364 * @return array
365 * @throws ApiUsageException
366 */
367 private function extractAllSlotInfo( RevisionRecord $revision, $revDel ): array {
368 $vals = [];
369
370 if ( $this->slotRoles === null ) {
371 try {
372 $slot = $revision->getSlot( SlotRecord::MAIN, RevisionRecord::RAW );
373 } catch ( RevisionAccessException $e ) {
374 // Back compat: If there's no slot, there's no content, so set 'textmissing'
375 // @todo: Gergő says to mention T198099 as a "todo" here.
376 $vals['textmissing'] = true;
377 $slot = null;
378 }
379
380 if ( $slot ) {
381 $content = null;
382 $vals += $this->extractSlotInfo( $slot, $revDel, $content );
383 if ( !empty( $vals['nosuchsection'] ) ) {
384 $this->dieWithError(
385 [
386 'apierror-nosuchsection-what',
387 wfEscapeWikiText( $this->section ),
388 $this->msg( 'revid', $revision->getId() )
389 ],
390 'nosuchsection'
391 );
392 }
393 if ( $content ) {
394 $vals += $this->extractDeprecatedContent( $content, $revision );
395 }
396 }
397 } else {
398 $roles = array_intersect( $this->slotRoles, $revision->getSlotRoles() );
399 $vals['slots'] = [
400 ApiResult::META_KVP_MERGE => true,
401 ];
402 foreach ( $roles as $role ) {
403 try {
404 $slot = $revision->getSlot( $role, RevisionRecord::RAW );
405 } catch ( RevisionAccessException $e ) {
406 // Don't error out here so the client can still process other slots/revisions.
407 // @todo: Gergő says to mention T198099 as a "todo" here.
408 $vals['slots'][$role]['missing'] = true;
409 continue;
410 }
411 $content = null;
412 $vals['slots'][$role] = $this->extractSlotInfo( $slot, $revDel, $content );
413 // @todo Move this into extractSlotInfo() (and remove its $content parameter)
414 // when extractDeprecatedContent() is no more.
415 if ( $content ) {
416 $vals['slots'][$role]['contentmodel'] = $content->getModel();
417 $vals['slots'][$role]['contentformat'] = $content->getDefaultFormat();
418 ApiResult::setContentValue(
419 $vals['slots'][$role],
420 'content',
421 $content->serialize()
422 );
423 }
424 }
425 ApiResult::setArrayType( $vals['slots'], 'kvp', 'role' );
426 ApiResult::setIndexedTagName( $vals['slots'], 'slot' );
427 }
428 return $vals;
429 }
430
431 /**
432 * Extract information from the SlotRecord
433 *
434 * @param SlotRecord $slot
435 * @param int $revDel Revdel status flags, from self::checkRevDel()
436 * @param Content|null &$content Set to the slot's content, if available
437 * and $this->fetchContent is true
438 * @return array
439 */
440 private function extractSlotInfo( SlotRecord $slot, $revDel, &$content = null ) {
441 $vals = [];
442 ApiResult::setArrayType( $vals, 'assoc' );
443
444 if ( $this->fld_slotsize ) {
445 $vals['size'] = (int)$slot->getSize();
446 }
447
448 if ( $this->fld_slotsha1 ) {
449 if ( ( $revDel & self::IS_DELETED ) ) {
450 $vals['sha1hidden'] = true;
451 }
452 if ( !( $revDel & self::CANNOT_VIEW ) ) {
453 if ( $slot->getSha1() != '' ) {
454 $vals['sha1'] = Wikimedia\base_convert( $slot->getSha1(), 36, 16, 40 );
455 } else {
456 $vals['sha1'] = '';
457 }
458 }
459 }
460
461 if ( $this->fld_contentmodel ) {
462 $vals['contentmodel'] = $slot->getModel();
463 }
464
465 $content = null;
466 if ( $this->fetchContent ) {
467 if ( ( $revDel & self::IS_DELETED ) ) {
468 $vals['texthidden'] = true;
469 }
470 if ( !( $revDel & self::CANNOT_VIEW ) ) {
471 try {
472 $content = $slot->getContent();
473 } catch ( RevisionAccessException $e ) {
474 // @todo: Gergő says to mention T198099 as a "todo" here.
475 $vals['textmissing'] = true;
476 }
477 // Expand templates after getting section content because
478 // template-added sections don't count and Parser::preprocess()
479 // will have less input
480 if ( $content && $this->section !== false ) {
481 $content = $content->getSection( $this->section, false );
482 if ( !$content ) {
483 $vals['nosuchsection'] = true;
484 }
485 }
486 }
487 }
488
489 return $vals;
490 }
491
492 /**
493 * Format a Content using deprecated options
494 * @param Content $content Content to format
495 * @param RevisionRecord $revision Revision being processed
496 * @return array
497 */
498 private function extractDeprecatedContent( Content $content, RevisionRecord $revision ) {
499 $vals = [];
500 $title = Title::newFromLinkTarget( $revision->getPageAsLinkTarget() );
501
502 if ( $this->fld_parsetree || ( $this->fld_content && $this->generateXML ) ) {
503 if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
504 $t = $content->getText(); # note: don't set $text
505
506 $parser = MediaWikiServices::getInstance()->getParser();
507 $parser->startExternalParse(
508 $title,
509 ParserOptions::newFromContext( $this->getContext() ),
510 Parser::OT_PREPROCESS
511 );
512 $dom = $parser->preprocessToDom( $t );
513 if ( is_callable( [ $dom, 'saveXML' ] ) ) {
514 $xml = $dom->saveXML();
515 } else {
516 $xml = $dom->__toString();
517 }
518 $vals['parsetree'] = $xml;
519 } else {
520 $vals['badcontentformatforparsetree'] = true;
521 $this->addWarning(
522 [
523 'apierror-parsetree-notwikitext-title',
524 wfEscapeWikiText( $title->getPrefixedText() ),
525 $content->getModel()
526 ],
527 'parsetree-notwikitext'
528 );
529 }
530 }
531
532 if ( $this->fld_content ) {
533 $text = null;
534
535 if ( $this->expandTemplates && !$this->parseContent ) {
536 if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
537 $text = $content->getText();
538
539 $text = MediaWikiServices::getInstance()->getParser()->preprocess(
540 $text,
541 $title,
542 ParserOptions::newFromContext( $this->getContext() )
543 );
544 } else {
545 $this->addWarning( [
546 'apierror-templateexpansion-notwikitext',
547 wfEscapeWikiText( $title->getPrefixedText() ),
548 $content->getModel()
549 ] );
550 $vals['badcontentformat'] = true;
551 $text = false;
552 }
553 }
554 if ( $this->parseContent ) {
555 $po = $content->getParserOutput(
556 $title,
557 $revision->getId(),
558 ParserOptions::newFromContext( $this->getContext() )
559 );
560 $text = $po->getText();
561 }
562
563 if ( $text === null ) {
564 $format = $this->contentFormat ?: $content->getDefaultFormat();
565 $model = $content->getModel();
566
567 if ( !$content->isSupportedFormat( $format ) ) {
568 $name = wfEscapeWikiText( $title->getPrefixedText() );
569 $this->addWarning( [ 'apierror-badformat', $this->contentFormat, $model, $name ] );
570 $vals['badcontentformat'] = true;
571 $text = false;
572 } else {
573 $text = $content->serialize( $format );
574 // always include format and model.
575 // Format is needed to deserialize, model is needed to interpret.
576 $vals['contentformat'] = $format;
577 $vals['contentmodel'] = $model;
578 }
579 }
580
581 if ( $text !== false ) {
582 ApiResult::setContentValue( $vals, 'content', $text );
583 }
584 }
585
586 if ( $content && ( !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) ) {
587 static $n = 0; // Number of uncached diffs we've had
588
589 if ( $n < $this->getConfig()->get( 'APIMaxUncachedDiffs' ) ) {
590 $vals['diff'] = [];
591 $context = new DerivativeContext( $this->getContext() );
592 $context->setTitle( $title );
593 $handler = $content->getContentHandler();
594
595 if ( !is_null( $this->difftotext ) ) {
596 $model = $title->getContentModel();
597
598 if ( $this->contentFormat
599 && !ContentHandler::getForModelID( $model )->isSupportedFormat( $this->contentFormat )
600 ) {
601 $name = wfEscapeWikiText( $title->getPrefixedText() );
602 $this->addWarning( [ 'apierror-badformat', $this->contentFormat, $model, $name ] );
603 $vals['diff']['badcontentformat'] = true;
604 $engine = null;
605 } else {
606 $difftocontent = ContentHandler::makeContent(
607 $this->difftotext,
608 $title,
609 $model,
610 $this->contentFormat
611 );
612
613 if ( $this->difftotextpst ) {
614 $popts = ParserOptions::newFromContext( $this->getContext() );
615 $difftocontent = $difftocontent->preSaveTransform( $title, $this->getUser(), $popts );
616 }
617
618 $engine = $handler->createDifferenceEngine( $context );
619 $engine->setContent( $content, $difftocontent );
620 }
621 } else {
622 $engine = $handler->createDifferenceEngine( $context, $revision->getId(), $this->diffto );
623 $vals['diff']['from'] = $engine->getOldid();
624 $vals['diff']['to'] = $engine->getNewid();
625 }
626 if ( $engine ) {
627 $difftext = $engine->getDiffBody();
628 ApiResult::setContentValue( $vals['diff'], 'body', $difftext );
629 if ( !$engine->wasCacheHit() ) {
630 $n++;
631 }
632 }
633 } else {
634 $vals['diff']['notcached'] = true;
635 }
636 }
637
638 return $vals;
639 }
640
641 public function getCacheMode( $params ) {
642 if ( $this->userCanSeeRevDel() ) {
643 return 'private';
644 }
645
646 return 'public';
647 }
648
649 public function getAllowedParams() {
650 $slotRoles = MediaWikiServices::getInstance()->getSlotRoleRegistry()->getKnownRoles();
651 sort( $slotRoles, SORT_STRING );
652
653 return [
654 'prop' => [
655 ApiBase::PARAM_ISMULTI => true,
656 ApiBase::PARAM_DFLT => 'ids|timestamp|flags|comment|user',
657 ApiBase::PARAM_TYPE => [
658 'ids',
659 'flags',
660 'timestamp',
661 'user',
662 'userid',
663 'size',
664 'slotsize',
665 'sha1',
666 'slotsha1',
667 'contentmodel',
668 'comment',
669 'parsedcomment',
670 'content',
671 'tags',
672 'roles',
673 'parsetree',
674 ],
675 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-prop',
676 ApiBase::PARAM_HELP_MSG_PER_VALUE => [
677 'ids' => 'apihelp-query+revisions+base-paramvalue-prop-ids',
678 'flags' => 'apihelp-query+revisions+base-paramvalue-prop-flags',
679 'timestamp' => 'apihelp-query+revisions+base-paramvalue-prop-timestamp',
680 'user' => 'apihelp-query+revisions+base-paramvalue-prop-user',
681 'userid' => 'apihelp-query+revisions+base-paramvalue-prop-userid',
682 'size' => 'apihelp-query+revisions+base-paramvalue-prop-size',
683 'slotsize' => 'apihelp-query+revisions+base-paramvalue-prop-slotsize',
684 'sha1' => 'apihelp-query+revisions+base-paramvalue-prop-sha1',
685 'slotsha1' => 'apihelp-query+revisions+base-paramvalue-prop-slotsha1',
686 'contentmodel' => 'apihelp-query+revisions+base-paramvalue-prop-contentmodel',
687 'comment' => 'apihelp-query+revisions+base-paramvalue-prop-comment',
688 'parsedcomment' => 'apihelp-query+revisions+base-paramvalue-prop-parsedcomment',
689 'content' => 'apihelp-query+revisions+base-paramvalue-prop-content',
690 'tags' => 'apihelp-query+revisions+base-paramvalue-prop-tags',
691 'roles' => 'apihelp-query+revisions+base-paramvalue-prop-roles',
692 'parsetree' => [ 'apihelp-query+revisions+base-paramvalue-prop-parsetree',
693 CONTENT_MODEL_WIKITEXT ],
694 ],
695 ApiBase::PARAM_DEPRECATED_VALUES => [
696 'parsetree' => true,
697 ],
698 ],
699 'slots' => [
700 ApiBase::PARAM_TYPE => $slotRoles,
701 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-slots',
702 ApiBase::PARAM_ISMULTI => true,
703 ApiBase::PARAM_ALL => true,
704 ],
705 'limit' => [
706 ApiBase::PARAM_TYPE => 'limit',
707 ApiBase::PARAM_MIN => 1,
708 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
709 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2,
710 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-limit',
711 ],
712 'expandtemplates' => [
713 ApiBase::PARAM_DFLT => false,
714 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-expandtemplates',
715 ApiBase::PARAM_DEPRECATED => true,
716 ],
717 'generatexml' => [
718 ApiBase::PARAM_DFLT => false,
719 ApiBase::PARAM_DEPRECATED => true,
720 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-generatexml',
721 ],
722 'parse' => [
723 ApiBase::PARAM_DFLT => false,
724 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-parse',
725 ApiBase::PARAM_DEPRECATED => true,
726 ],
727 'section' => [
728 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-section',
729 ],
730 'diffto' => [
731 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-diffto',
732 ApiBase::PARAM_DEPRECATED => true,
733 ],
734 'difftotext' => [
735 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-difftotext',
736 ApiBase::PARAM_DEPRECATED => true,
737 ],
738 'difftotextpst' => [
739 ApiBase::PARAM_DFLT => false,
740 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-difftotextpst',
741 ApiBase::PARAM_DEPRECATED => true,
742 ],
743 'contentformat' => [
744 ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
745 ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-contentformat',
746 ApiBase::PARAM_DEPRECATED => true,
747 ],
748 ];
749 }
750
751 }