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