MediaWiki REL1_32
ApiComparePages.php
Go to the documentation of this file.
1<?php
27
28class ApiComparePages extends ApiBase {
29
32
33 private $guessedTitle = false, $props;
34
35 public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
36 parent::__construct( $mainModule, $moduleName, $modulePrefix );
37 $this->revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
38 }
39
40 public function execute() {
42
43 // Parameter validation
45 $params, 'fromtitle', 'fromid', 'fromrev', 'fromtext', 'fromslots'
46 );
48 $params, 'totitle', 'toid', 'torev', 'totext', 'torelative', 'toslots'
49 );
50
51 $this->props = array_flip( $params['prop'] );
52
53 // Cache responses publicly by default. This may be overridden later.
54 $this->getMain()->setCacheMode( 'public' );
55
56 // Get the 'from' RevisionRecord
57 list( $fromRev, $fromRelRev, $fromValsRev ) = $this->getDiffRevision( 'from', $params );
58
59 // Get the 'to' RevisionRecord
60 if ( $params['torelative'] !== null ) {
61 if ( !$fromRelRev ) {
62 $this->dieWithError( 'apierror-compare-relative-to-nothing' );
63 }
64 switch ( $params['torelative'] ) {
65 case 'prev':
66 // Swap 'from' and 'to'
67 list( $toRev, $toRelRev, $toValsRev ) = [ $fromRev, $fromRelRev, $fromValsRev ];
68 $fromRev = $this->revisionStore->getPreviousRevision( $toRelRev );
69 $fromRelRev = $fromRev;
70 $fromValsRev = $fromRev;
71 if ( !$fromRev ) {
72 $title = Title::newFromLinkTarget( $toRelRev->getPageAsLinkTarget() );
73 $this->addWarning( [
74 'apiwarn-compare-no-prev',
75 wfEscapeWikiText( $title->getPrefixedText() ),
76 $toRelRev->getId()
77 ] );
78
79 // (T203433) Create an empty dummy revision as the "previous".
80 // The main slot has to exist, the rest will be handled by DifferenceEngine.
81 $fromRev = $this->revisionStore->newMutableRevisionFromArray( [
82 'title' => $title ?: Title::makeTitle( NS_SPECIAL, 'Badtitle/' . __METHOD__ )
83 ] );
84 $fromRev->setContent(
85 SlotRecord::MAIN,
86 $toRelRev->getContent( SlotRecord::MAIN, RevisionRecord::RAW )
87 ->getContentHandler()
88 ->makeEmptyContent()
89 );
90 }
91 break;
92
93 case 'next':
94 $toRev = $this->revisionStore->getNextRevision( $fromRelRev );
95 $toRelRev = $toRev;
96 $toValsRev = $toRev;
97 if ( !$toRev ) {
98 $title = Title::newFromLinkTarget( $fromRelRev->getPageAsLinkTarget() );
99 $this->addWarning( [
100 'apiwarn-compare-no-next',
101 wfEscapeWikiText( $title->getPrefixedText() ),
102 $fromRelRev->getId()
103 ] );
104
105 // (T203433) The web UI treats "next" as "cur" in this case.
106 // Avoid repeating metadata by making a MutableRevisionRecord with no changes.
107 $toRev = MutableRevisionRecord::newFromParentRevision( $fromRelRev );
108 }
109 break;
110
111 case 'cur':
112 $title = $fromRelRev->getPageAsLinkTarget();
113 $toRev = $this->revisionStore->getRevisionByTitle( $title );
114 if ( !$toRev ) {
115 $title = Title::newFromLinkTarget( $title );
116 $this->dieWithError(
117 [ 'apierror-missingrev-title', wfEscapeWikiText( $title->getPrefixedText() ) ], 'nosuchrevid'
118 );
119 }
120 $toRelRev = $toRev;
121 $toValsRev = $toRev;
122 break;
123 }
124 } else {
125 list( $toRev, $toRelRev, $toValsRev ) = $this->getDiffRevision( 'to', $params );
126 }
127
128 // Handle missing from or to revisions (should never happen)
129 // @codeCoverageIgnoreStart
130 if ( !$fromRev || !$toRev ) {
131 $this->dieWithError( 'apierror-baddiff' );
132 }
133 // @codeCoverageIgnoreEnd
134
135 // Handle revdel
136 if ( !$fromRev->audienceCan(
137 RevisionRecord::DELETED_TEXT, RevisionRecord::FOR_THIS_USER, $this->getUser()
138 ) ) {
139 $this->dieWithError( [ 'apierror-missingcontent-revid', $fromRev->getId() ], 'missingcontent' );
140 }
141 if ( !$toRev->audienceCan(
142 RevisionRecord::DELETED_TEXT, RevisionRecord::FOR_THIS_USER, $this->getUser()
143 ) ) {
144 $this->dieWithError( [ 'apierror-missingcontent-revid', $toRev->getId() ], 'missingcontent' );
145 }
146
147 // Get the diff
148 $context = new DerivativeContext( $this->getContext() );
149 if ( $fromRelRev && $fromRelRev->getPageAsLinkTarget() ) {
150 $context->setTitle( Title::newFromLinkTarget( $fromRelRev->getPageAsLinkTarget() ) );
151 } elseif ( $toRelRev && $toRelRev->getPageAsLinkTarget() ) {
152 $context->setTitle( Title::newFromLinkTarget( $toRelRev->getPageAsLinkTarget() ) );
153 } else {
154 $guessedTitle = $this->guessTitle();
155 if ( $guessedTitle ) {
156 $context->setTitle( $guessedTitle );
157 }
158 }
159 $de = new DifferenceEngine( $context );
160 $de->setRevisions( $fromRev, $toRev );
161 if ( $params['slots'] === null ) {
162 $difftext = $de->getDiffBody();
163 if ( $difftext === false ) {
164 $this->dieWithError( 'apierror-baddiff' );
165 }
166 } else {
167 $difftext = [];
168 foreach ( $params['slots'] as $role ) {
169 $difftext[$role] = $de->getDiffBodyForRole( $role );
170 }
171 }
172
173 // Fill in the response
174 $vals = [];
175 $this->setVals( $vals, 'from', $fromValsRev );
176 $this->setVals( $vals, 'to', $toValsRev );
177
178 if ( isset( $this->props['rel'] ) ) {
179 if ( !$fromRev instanceof MutableRevisionRecord ) {
180 $rev = $this->revisionStore->getPreviousRevision( $fromRev );
181 if ( $rev ) {
182 $vals['prev'] = $rev->getId();
183 }
184 }
185 if ( !$toRev instanceof MutableRevisionRecord ) {
186 $rev = $this->revisionStore->getNextRevision( $toRev );
187 if ( $rev ) {
188 $vals['next'] = $rev->getId();
189 }
190 }
191 }
192
193 if ( isset( $this->props['diffsize'] ) ) {
194 $vals['diffsize'] = 0;
195 foreach ( (array)$difftext as $text ) {
196 $vals['diffsize'] += strlen( $text );
197 }
198 }
199 if ( isset( $this->props['diff'] ) ) {
200 if ( is_array( $difftext ) ) {
201 ApiResult::setArrayType( $difftext, 'kvp', 'diff' );
202 $vals['bodies'] = $difftext;
203 } else {
204 ApiResult::setContentValue( $vals, 'body', $difftext );
205 }
206 }
207
208 // Diffs can be really big and there's little point in having
209 // ApiResult truncate it to an empty response since the diff is the
210 // whole reason this module exists. So pass NO_SIZE_CHECK here.
211 $this->getResult()->addValue( null, $this->getModuleName(), $vals, ApiResult::NO_SIZE_CHECK );
212 }
213
222 private function getRevisionById( $id ) {
223 $rev = $this->revisionStore->getRevisionById( $id );
224 if ( !$rev && $this->getUser()->isAllowedAny( 'deletedtext', 'undelete' ) ) {
225 // Try the 'archive' table
226 $arQuery = $this->revisionStore->getArchiveQueryInfo();
227 $row = $this->getDB()->selectRow(
228 $arQuery['tables'],
229 array_merge(
230 $arQuery['fields'],
231 [ 'ar_namespace', 'ar_title' ]
232 ),
233 [ 'ar_rev_id' => $id ],
234 __METHOD__,
235 [],
236 $arQuery['joins']
237 );
238 if ( $row ) {
239 $rev = $this->revisionStore->newRevisionFromArchiveRow( $row );
240 $rev->isArchive = true;
241 }
242 }
243 return $rev;
244 }
245
251 private function guessTitle() {
252 if ( $this->guessedTitle !== false ) {
253 return $this->guessedTitle;
254 }
255
256 $this->guessedTitle = null;
257 $params = $this->extractRequestParams();
258
259 foreach ( [ 'from', 'to' ] as $prefix ) {
260 if ( $params["{$prefix}rev"] !== null ) {
261 $rev = $this->getRevisionById( $params["{$prefix}rev"] );
262 if ( $rev ) {
263 $this->guessedTitle = Title::newFromLinkTarget( $rev->getPageAsLinkTarget() );
264 break;
265 }
266 }
267
268 if ( $params["{$prefix}title"] !== null ) {
269 $title = Title::newFromText( $params["{$prefix}title"] );
270 if ( $title && !$title->isExternal() ) {
271 $this->guessedTitle = $title;
272 break;
273 }
274 }
275
276 if ( $params["{$prefix}id"] !== null ) {
277 $title = Title::newFromID( $params["{$prefix}id"] );
278 if ( $title ) {
279 $this->guessedTitle = $title;
280 break;
281 }
282 }
283 }
284
285 return $this->guessedTitle;
286 }
287
293 private function guessModel( $role ) {
294 $params = $this->extractRequestParams();
295
296 $title = null;
297 foreach ( [ 'from', 'to' ] as $prefix ) {
298 if ( $params["{$prefix}rev"] !== null ) {
299 $rev = $this->getRevisionById( $params["{$prefix}rev"] );
300 if ( $rev ) {
301 if ( $rev->hasSlot( $role ) ) {
302 return $rev->getSlot( $role, RevisionRecord::RAW )->getModel();
303 }
304 }
305 }
306 }
307
308 $guessedTitle = $this->guessTitle();
309 if ( $guessedTitle && $role === SlotRecord::MAIN ) {
310 // @todo: Use SlotRoleRegistry and do this for all slots
311 return $guessedTitle->getContentModel();
312 }
313
314 if ( isset( $params["fromcontentmodel-$role"] ) ) {
315 return $params["fromcontentmodel-$role"];
316 }
317 if ( isset( $params["tocontentmodel-$role"] ) ) {
318 return $params["tocontentmodel-$role"];
319 }
320
321 if ( $role === SlotRecord::MAIN ) {
322 if ( isset( $params['fromcontentmodel'] ) ) {
323 return $params['fromcontentmodel'];
324 }
325 if ( isset( $params['tocontentmodel'] ) ) {
326 return $params['tocontentmodel'];
327 }
328 }
329
330 return null;
331 }
332
348 private function getDiffRevision( $prefix, array $params ) {
349 // Back compat params
350 $this->requireMaxOneParameter( $params, "{$prefix}text", "{$prefix}slots" );
351 $this->requireMaxOneParameter( $params, "{$prefix}section", "{$prefix}slots" );
352 if ( $params["{$prefix}text"] !== null ) {
353 $params["{$prefix}slots"] = [ SlotRecord::MAIN ];
354 $params["{$prefix}text-main"] = $params["{$prefix}text"];
355 $params["{$prefix}section-main"] = null;
356 $params["{$prefix}contentmodel-main"] = $params["{$prefix}contentmodel"];
357 $params["{$prefix}contentformat-main"] = $params["{$prefix}contentformat"];
358 }
359
360 $title = null;
361 $rev = null;
362 $suppliedContent = $params["{$prefix}slots"] !== null;
363
364 // Get the revision and title, if applicable
365 $revId = null;
366 if ( $params["{$prefix}rev"] !== null ) {
367 $revId = $params["{$prefix}rev"];
368 } elseif ( $params["{$prefix}title"] !== null || $params["{$prefix}id"] !== null ) {
369 if ( $params["{$prefix}title"] !== null ) {
370 $title = Title::newFromText( $params["{$prefix}title"] );
371 if ( !$title || $title->isExternal() ) {
372 $this->dieWithError(
373 [ 'apierror-invalidtitle', wfEscapeWikiText( $params["{$prefix}title"] ) ]
374 );
375 }
376 } else {
377 $title = Title::newFromID( $params["{$prefix}id"] );
378 if ( !$title ) {
379 $this->dieWithError( [ 'apierror-nosuchpageid', $params["{$prefix}id"] ] );
380 }
381 }
382 $revId = $title->getLatestRevID();
383 if ( !$revId ) {
384 $revId = null;
385 // Only die here if we're not using supplied text
386 if ( !$suppliedContent ) {
387 if ( $title->exists() ) {
388 $this->dieWithError(
389 [ 'apierror-missingrev-title', wfEscapeWikiText( $title->getPrefixedText() ) ], 'nosuchrevid'
390 );
391 } else {
392 $this->dieWithError(
393 [ 'apierror-missingtitle-byname', wfEscapeWikiText( $title->getPrefixedText() ) ],
394 'missingtitle'
395 );
396 }
397 }
398 }
399 }
400 if ( $revId !== null ) {
401 $rev = $this->getRevisionById( $revId );
402 if ( !$rev ) {
403 $this->dieWithError( [ 'apierror-nosuchrevid', $revId ] );
404 }
405 $title = Title::newFromLinkTarget( $rev->getPageAsLinkTarget() );
406
407 // If we don't have supplied content, return here. Otherwise,
408 // continue on below with the supplied content.
409 if ( !$suppliedContent ) {
410 $newRev = $rev;
411
412 // Deprecated 'fromsection'/'tosection'
413 if ( isset( $params["{$prefix}section"] ) ) {
414 $section = $params["{$prefix}section"];
415 $newRev = MutableRevisionRecord::newFromParentRevision( $rev );
416 $content = $rev->getContent( SlotRecord::MAIN, RevisionRecord::FOR_THIS_USER,
417 $this->getUser() );
418 if ( !$content ) {
419 $this->dieWithError(
420 [ 'apierror-missingcontent-revid-role', $rev->getId(), SlotRecord::MAIN ], 'missingcontent'
421 );
422 }
423 $content = $content ? $content->getSection( $section ) : null;
424 if ( !$content ) {
425 $this->dieWithError(
426 [ "apierror-compare-nosuch{$prefix}section", wfEscapeWikiText( $section ) ],
427 "nosuch{$prefix}section"
428 );
429 }
430 $newRev->setContent( SlotRecord::MAIN, $content );
431 }
432
433 return [ $newRev, $rev, $rev ];
434 }
435 }
436
437 // Override $content based on supplied text
438 if ( !$title ) {
439 $title = $this->guessTitle();
440 }
441 if ( $rev ) {
442 $newRev = MutableRevisionRecord::newFromParentRevision( $rev );
443 } else {
444 $newRev = $this->revisionStore->newMutableRevisionFromArray( [
445 'title' => $title ?: Title::makeTitle( NS_SPECIAL, 'Badtitle/' . __METHOD__ )
446 ] );
447 }
448 foreach ( $params["{$prefix}slots"] as $role ) {
449 $text = $params["{$prefix}text-{$role}"];
450 if ( $text === null ) {
451 // The SlotRecord::MAIN role can't be deleted
452 if ( $role === SlotRecord::MAIN ) {
453 $this->dieWithError( [ 'apierror-compare-maintextrequired', $prefix ] );
454 }
455
456 // These parameters make no sense without text. Reject them to avoid
457 // confusion.
458 foreach ( [ 'section', 'contentmodel', 'contentformat' ] as $param ) {
459 if ( isset( $params["{$prefix}{$param}-{$role}"] ) ) {
460 $this->dieWithError( [
461 'apierror-compare-notext',
462 wfEscapeWikiText( "{$prefix}{$param}-{$role}" ),
463 wfEscapeWikiText( "{$prefix}text-{$role}" ),
464 ] );
465 }
466 }
467
468 $newRev->removeSlot( $role );
469 continue;
470 }
471
472 $model = $params["{$prefix}contentmodel-{$role}"];
473 $format = $params["{$prefix}contentformat-{$role}"];
474
475 if ( !$model && $rev && $rev->hasSlot( $role ) ) {
476 $model = $rev->getSlot( $role, RevisionRecord::RAW )->getModel();
477 }
478 if ( !$model && $title && $role === SlotRecord::MAIN ) {
479 // @todo: Use SlotRoleRegistry and do this for all slots
480 $model = $title->getContentModel();
481 }
482 if ( !$model ) {
483 $model = $this->guessModel( $role );
484 }
485 if ( !$model ) {
486 $model = CONTENT_MODEL_WIKITEXT;
487 $this->addWarning( [ 'apiwarn-compare-nocontentmodel', $model ] );
488 }
489
490 try {
491 $content = ContentHandler::makeContent( $text, $title, $model, $format );
492 } catch ( MWContentSerializationException $ex ) {
493 $this->dieWithException( $ex, [
494 'wrap' => ApiMessage::create( 'apierror-contentserializationexception', 'parseerror' )
495 ] );
496 }
497
498 if ( $params["{$prefix}pst"] ) {
499 if ( !$title ) {
500 $this->dieWithError( 'apierror-compare-no-title' );
501 }
502 $popts = ParserOptions::newFromContext( $this->getContext() );
503 $content = $content->preSaveTransform( $title, $this->getUser(), $popts );
504 }
505
506 $section = $params["{$prefix}section-{$role}"];
507 if ( $section !== null && $section !== '' ) {
508 if ( !$rev ) {
509 $this->dieWithError( "apierror-compare-no{$prefix}revision" );
510 }
511 $oldContent = $rev->getContent( $role, RevisionRecord::FOR_THIS_USER, $this->getUser() );
512 if ( !$oldContent ) {
513 $this->dieWithError(
514 [ 'apierror-missingcontent-revid-role', $rev->getId(), wfEscapeWikiText( $role ) ],
515 'missingcontent'
516 );
517 }
518 if ( !$oldContent->getContentHandler()->supportsSections() ) {
519 $this->dieWithError( [ 'apierror-sectionsnotsupported', $content->getModel() ] );
520 }
521 try {
522 $content = $oldContent->replaceSection( $section, $content, '' );
523 } catch ( Exception $ex ) {
524 // Probably a content model mismatch.
525 $content = null;
526 }
527 if ( !$content ) {
528 $this->dieWithError( [ 'apierror-sectionreplacefailed' ] );
529 }
530 }
531
532 // Deprecated 'fromsection'/'tosection'
533 if ( $role === SlotRecord::MAIN && isset( $params["{$prefix}section"] ) ) {
534 $section = $params["{$prefix}section"];
535 $content = $content->getSection( $section );
536 if ( !$content ) {
537 $this->dieWithError(
538 [ "apierror-compare-nosuch{$prefix}section", wfEscapeWikiText( $section ) ],
539 "nosuch{$prefix}section"
540 );
541 }
542 }
543
544 $newRev->setContent( $role, $content );
545 }
546 return [ $newRev, $rev, null ];
547 }
548
556 private function setVals( &$vals, $prefix, $rev ) {
557 if ( $rev ) {
558 $title = $rev->getPageAsLinkTarget();
559 if ( isset( $this->props['ids'] ) ) {
560 $vals["{$prefix}id"] = $title->getArticleID();
561 $vals["{$prefix}revid"] = $rev->getId();
562 }
563 if ( isset( $this->props['title'] ) ) {
564 ApiQueryBase::addTitleInfo( $vals, $title, $prefix );
565 }
566 if ( isset( $this->props['size'] ) ) {
567 $vals["{$prefix}size"] = $rev->getSize();
568 }
569
570 $anyHidden = false;
571 if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
572 $vals["{$prefix}texthidden"] = true;
573 $anyHidden = true;
574 }
575
576 if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) ) {
577 $vals["{$prefix}userhidden"] = true;
578 $anyHidden = true;
579 }
580 if ( isset( $this->props['user'] ) ) {
581 $user = $rev->getUser( RevisionRecord::FOR_THIS_USER, $this->getUser() );
582 if ( $user ) {
583 $vals["{$prefix}user"] = $user->getName();
584 $vals["{$prefix}userid"] = $user->getId();
585 }
586 }
587
588 if ( $rev->isDeleted( RevisionRecord::DELETED_COMMENT ) ) {
589 $vals["{$prefix}commenthidden"] = true;
590 $anyHidden = true;
591 }
592 if ( isset( $this->props['comment'] ) || isset( $this->props['parsedcomment'] ) ) {
593 $comment = $rev->getComment( RevisionRecord::FOR_THIS_USER, $this->getUser() );
594 if ( $comment !== null ) {
595 if ( isset( $this->props['comment'] ) ) {
596 $vals["{$prefix}comment"] = $comment->text;
597 }
598 $vals["{$prefix}parsedcomment"] = Linker::formatComment(
599 $comment->text, Title::newFromLinkTarget( $title )
600 );
601 }
602 }
603
604 if ( $anyHidden ) {
605 $this->getMain()->setCacheMode( 'private' );
606 if ( $rev->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
607 $vals["{$prefix}suppressed"] = true;
608 }
609 }
610
611 if ( !empty( $rev->isArchive ) ) {
612 $this->getMain()->setCacheMode( 'private' );
613 $vals["{$prefix}archive"] = true;
614 }
615 }
616 }
617
618 public function getAllowedParams() {
619 $slotRoles = MediaWikiServices::getInstance()->getSlotRoleStore()->getMap();
620 if ( !in_array( SlotRecord::MAIN, $slotRoles, true ) ) {
621 $slotRoles[] = SlotRecord::MAIN;
622 }
623 sort( $slotRoles, SORT_STRING );
624
625 // Parameters for the 'from' and 'to' content
626 $fromToParams = [
627 'title' => null,
628 'id' => [
629 ApiBase::PARAM_TYPE => 'integer'
630 ],
631 'rev' => [
632 ApiBase::PARAM_TYPE => 'integer'
633 ],
634
635 'slots' => [
636 ApiBase::PARAM_TYPE => $slotRoles,
638 ],
639 'text-{slot}' => [
640 ApiBase::PARAM_TEMPLATE_VARS => [ 'slot' => 'slots' ], // fixed below
641 ApiBase::PARAM_TYPE => 'text',
642 ],
643 'section-{slot}' => [
644 ApiBase::PARAM_TEMPLATE_VARS => [ 'slot' => 'slots' ], // fixed below
645 ApiBase::PARAM_TYPE => 'string',
646 ],
647 'contentformat-{slot}' => [
648 ApiBase::PARAM_TEMPLATE_VARS => [ 'slot' => 'slots' ], // fixed below
649 ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
650 ],
651 'contentmodel-{slot}' => [
652 ApiBase::PARAM_TEMPLATE_VARS => [ 'slot' => 'slots' ], // fixed below
653 ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
654 ],
655 'pst' => false,
656
657 'text' => [
658 ApiBase::PARAM_TYPE => 'text',
660 ],
661 'contentformat' => [
662 ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
664 ],
665 'contentmodel' => [
666 ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
668 ],
669 'section' => [
670 ApiBase::PARAM_DFLT => null,
672 ],
673 ];
674
675 $ret = [];
676 foreach ( $fromToParams as $k => $v ) {
677 if ( isset( $v[ApiBase::PARAM_TEMPLATE_VARS]['slot'] ) ) {
678 $v[ApiBase::PARAM_TEMPLATE_VARS]['slot'] = 'fromslots';
679 }
680 $ret["from$k"] = $v;
681 }
682 foreach ( $fromToParams as $k => $v ) {
683 if ( isset( $v[ApiBase::PARAM_TEMPLATE_VARS]['slot'] ) ) {
684 $v[ApiBase::PARAM_TEMPLATE_VARS]['slot'] = 'toslots';
685 }
686 $ret["to$k"] = $v;
687 }
688
690 $ret,
691 [ 'torelative' => [ ApiBase::PARAM_TYPE => [ 'prev', 'next', 'cur' ], ] ],
692 'torev'
693 );
694
695 $ret['prop'] = [
696 ApiBase::PARAM_DFLT => 'diff|ids|title',
698 'diff',
699 'diffsize',
700 'rel',
701 'ids',
702 'title',
703 'user',
704 'comment',
705 'parsedcomment',
706 'size',
707 ],
710 ];
711
712 $ret['slots'] = [
713 ApiBase::PARAM_TYPE => $slotRoles,
715 ApiBase::PARAM_ALL => true,
716 ];
717
718 return $ret;
719 }
720
721 protected function getExamplesMessages() {
722 return [
723 'action=compare&fromrev=1&torev=2'
724 => 'apihelp-compare-example-1',
725 ];
726 }
727}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfArrayInsertAfter(array $array, array $insert, $after)
Insert array into another array after the specified KEY
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:37
const PARAM_DEPRECATED
(boolean) Is the parameter deprecated (will show a warning)?
Definition ApiBase.php:105
getDB()
Gets a default replica DB connection object.
Definition ApiBase.php:687
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition ApiBase.php:1987
getMain()
Get the main module.
Definition ApiBase.php:555
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
Definition ApiBase.php:87
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition ApiBase.php:48
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition ApiBase.php:157
const PARAM_TEMPLATE_VARS
(array) Indicate that this is a templated parameter, and specify replacements.
Definition ApiBase.php:245
getResult()
Get the result object.
Definition ApiBase.php:659
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:770
requireMaxOneParameter( $params, $required)
Die if more than one of a certain set of parameters is set and not false.
Definition ApiBase.php:939
dieWithException( $exception, array $options=[])
Abort execution with an error derived from an exception.
Definition ApiBase.php:1999
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition ApiBase.php:1906
requireAtLeastOneParameter( $params, $required)
Die if none of a certain set of parameters is set and not false.
Definition ApiBase.php:967
const PARAM_ALL
(boolean|string) When PARAM_TYPE has a defined set of values and PARAM_ISMULTI is true,...
Definition ApiBase.php:180
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:539
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition ApiBase.php:51
getDiffRevision( $prefix, array $params)
Get the RevisionRecord for one side of the diff.
getExamplesMessages()
Returns usage examples for this module.
setVals(&$vals, $prefix, $rev)
Set value fields from a RevisionRecord object.
getRevisionById( $id)
Load a revision by ID.
__construct(ApiMain $mainModule, $moduleName, $modulePrefix='')
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
RevisionStore $revisionStore
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
guessTitle()
Guess an appropriate default Title for this request.
guessModel( $role)
Guess an appropriate default content model for this request.
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:41
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
const NO_SIZE_CHECK
For addValue() and similar functions, do not check size while adding a value Don't use this unless yo...
Definition ApiResult.php:58
static setContentValue(array &$arr, $name, $value, $flags=0)
Add an output value to the array by name and mark as META_CONTENT.
IContextSource $context
getContext()
Get the base IContextSource object.
An IContextSource implementation which will inherit context from another source but allow individual ...
DifferenceEngine is responsible for rendering the difference between two revisions as HTML.
static formatComment( $comment, $title=null, $local=false, $wikiId=null)
This function is called by all recent changes variants, by the page history, and by the user contribu...
Definition Linker.php:1088
Exception representing a failure to serialize or unserialize a content object.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Mutable RevisionRecord implementation, for building new revision entries programmatically.
Page revision base class.
Service for looking up page revisions.
Value object representing a content slot associated with a page revision.
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition deferred.txt:11
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
const NS_SPECIAL
Definition Defines.php:53
const CONTENT_MODEL_WIKITEXT
Definition Defines.php:235
namespace and then decline to actually register it file or subcat img or subcat $title
Definition hooks.txt:994
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition hooks.txt:2054
usually copyright or history_copyright This message must be in HTML not wikitext if the section is included from a template $section
Definition hooks.txt:3107
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
Definition hooks.txt:1818
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
Definition hooks.txt:247
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
$content
$newRev
$params