MediaWiki  1.32.0
ApiComparePages.php
Go to the documentation of this file.
1 <?php
27 
28 class ApiComparePages extends ApiBase {
29 
31  private $revisionStore;
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() {
41  $params = $this->extractRequestParams();
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 ) {
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,
637  ApiBase::PARAM_ISMULTI => true,
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
650  ],
651  'contentmodel-{slot}' => [
652  ApiBase::PARAM_TEMPLATE_VARS => [ 'slot' => 'slots' ], // fixed below
654  ],
655  'pst' => false,
656 
657  'text' => [
658  ApiBase::PARAM_TYPE => 'text',
660  ],
661  'contentformat' => [
664  ],
665  'contentmodel' => [
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  ],
708  ApiBase::PARAM_ISMULTI => true,
710  ];
711 
712  $ret['slots'] = [
713  ApiBase::PARAM_TYPE => $slotRoles,
714  ApiBase::PARAM_ISMULTI => true,
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 }
ApiMain
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:41
wfArrayInsertAfter
wfArrayInsertAfter(array $array, array $insert, $after)
Insert array into another array after the specified KEY
Definition: GlobalFunctions.php:229
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:33
$user
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 account $user
Definition: hooks.txt:244
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:280
ContextSource\getContext
getContext()
Get the base IContextSource object.
Definition: ContextSource.php:40
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:45
ContentHandler\getAllContentFormats
static getAllContentFormats()
Definition: ContentHandler.php:380
ApiBase\addWarning
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition: ApiBase.php:1906
ApiComparePages\setVals
setVals(&$vals, $prefix, $rev)
Set value fields from a RevisionRecord object.
Definition: ApiComparePages.php:556
Revision\RevisionStore
Service for looking up page revisions.
Definition: RevisionStore.php:76
ApiBase\dieWithError
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:1987
ApiBase\PARAM_ALL
const PARAM_ALL
(boolean|string) When PARAM_TYPE has a defined set of values and PARAM_ISMULTI is true,...
Definition: ApiBase.php:180
ApiComparePages\__construct
__construct(ApiMain $mainModule, $moduleName, $modulePrefix='')
Definition: ApiComparePages.php:35
ApiComparePages\$props
$props
Definition: ApiComparePages.php:33
ApiBase\PARAM_TYPE
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
Definition: ApiBase.php:87
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:659
ApiComparePages\$guessedTitle
$guessedTitle
Definition: ApiComparePages.php:33
$params
$params
Definition: styleTest.css.php:44
ApiBase\getDB
getDB()
Gets a default replica DB connection object.
Definition: ApiBase.php:687
ApiResult\NO_SIZE_CHECK
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
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:235
ContextSource\getUser
getUser()
Definition: ContextSource.php:120
php
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:35
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:37
ApiComparePages\guessModel
guessModel( $role)
Guess an appropriate default content model for this request.
Definition: ApiComparePages.php:293
ApiResult\setContentValue
static setContentValue(array &$arr, $name, $value, $flags=0)
Add an output value to the array by name and mark as META_CONTENT.
Definition: ApiResult.php:478
ApiBase\PARAM_DEPRECATED
const PARAM_DEPRECATED
(boolean) Is the parameter deprecated (will show a warning)?
Definition: ApiBase.php:105
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:53
DerivativeContext
An IContextSource implementation which will inherit context from another source but allow individual ...
Definition: DerivativeContext.php:30
ApiResult\setArrayType
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
Definition: ApiResult.php:728
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:964
$newRev
$newRev
Definition: pageupdater.txt:66
ContentHandler\getContentModels
static getContentModels()
Definition: ContentHandler.php:372
Title\newFromLinkTarget
static newFromLinkTarget(LinkTarget $linkTarget)
Create a new Title from a LinkTarget.
Definition: Title.php:251
ApiComparePages
Definition: ApiComparePages.php:28
MWContentSerializationException
Exception representing a failure to serialize or unserialize a content object.
Definition: MWContentSerializationException.php:7
ApiBase\dieWithException
dieWithException( $exception, array $options=[])
Abort execution with an error derived from an exception.
Definition: ApiBase.php:1999
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:770
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:545
ApiMessage\create
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
Definition: ApiMessage.php:40
array
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))
list
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
ContentHandler\makeContent
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
Definition: ContentHandler.php:133
ApiComparePages\guessTitle
guessTitle()
Guess an appropriate default Title for this request.
Definition: ApiComparePages.php:251
ApiComparePages\getDiffRevision
getDiffRevision( $prefix, array $params)
Get the RevisionRecord for one side of the diff.
Definition: ApiComparePages.php:348
ParserOptions\newFromContext
static newFromContext(IContextSource $context)
Get a ParserOptions object from a IContextSource object.
Definition: ParserOptions.php:1040
Revision\MutableRevisionRecord
Mutable RevisionRecord implementation, for building new revision entries programmatically.
Definition: MutableRevisionRecord.php:41
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1617
$ret
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:2036
ApiBase\requireMaxOneParameter
requireMaxOneParameter( $params, $required)
Die if more than one of a certain set of parameters is set and not false.
Definition: ApiBase.php:939
Linker\formatComment
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
ApiBase\requireAtLeastOneParameter
requireAtLeastOneParameter( $params, $required)
Die if none of a certain set of parameters is set and not false.
Definition: ApiBase.php:967
ApiBase\PARAM_TEMPLATE_VARS
const PARAM_TEMPLATE_VARS
(array) Indicate that this is a templated parameter, and specify replacements.
Definition: ApiBase.php:245
$section
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:3090
ApiBase\PARAM_DFLT
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition: ApiBase.php:48
ApiComparePages\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiComparePages.php:618
$rev
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:1808
as
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
Definition: distributors.txt:9
DifferenceEngine
DifferenceEngine is responsible for rendering the difference between two revisions as HTML.
Definition: DifferenceEngine.php:49
ApiComparePages\execute
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition: ApiComparePages.php:40
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:539
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition: ApiBase.php:51
ApiComparePages\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiComparePages.php:721
$content
$content
Definition: pageupdater.txt:72
ApiBase\getMain
getMain()
Get the main module.
Definition: ApiBase.php:555
MediaWikiServices
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 MediaWikiServices
Definition: injection.txt:23
ApiBase\PARAM_HELP_MSG_PER_VALUE
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
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:427
ApiComparePages\getRevisionById
getRevisionById( $id)
Load a revision by ID.
Definition: ApiComparePages.php:222
ApiQueryBase\addTitleInfo
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
Definition: ApiQueryBase.php:487
Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:39
ApiComparePages\$revisionStore
RevisionStore $revisionStore
Definition: ApiComparePages.php:31