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