MediaWiki  1.23.14
DifferenceEngine.php
Go to the documentation of this file.
1 <?php
30 define( 'MW_DIFF_VERSION', '1.11a' );
31 
37 
39  public $mOldid;
40 
42  public $mNewid;
43 
44  private $mOldTags;
45  private $mNewTags;
46 
48  public $mOldContent;
49 
51  public $mNewContent;
52 
54  protected $mDiffLang;
55 
57  public $mOldPage;
58 
60  public $mNewPage;
61 
63  public $mOldRev;
64 
66  public $mNewRev;
67 
69  private $mRevisionsIdsLoaded = false;
70 
72  public $mRevisionsLoaded = false;
73 
75  public $mTextLoaded = 0;
76 
78  public $mCacheHit = false;
79 
85  public $enableDebugComment = false;
86 
90  protected $mReducedLineNumbers = false;
91 
93  protected $mMarkPatrolledLink = null;
94 
96  protected $unhide = false;
108  public function __construct( $context = null, $old = 0, $new = 0, $rcid = 0,
109  $refreshCache = false, $unhide = false
110  ) {
111  if ( $context instanceof IContextSource ) {
112  $this->setContext( $context );
113  }
114 
115  wfDebug( "DifferenceEngine old '$old' new '$new' rcid '$rcid'\n" );
116 
117  $this->mOldid = $old;
118  $this->mNewid = $new;
119  $this->mRefreshCache = $refreshCache;
120  $this->unhide = $unhide;
121  }
122 
126  public function setReducedLineNumbers( $value = true ) {
127  $this->mReducedLineNumbers = $value;
128  }
129 
133  public function getDiffLang() {
134  if ( $this->mDiffLang === null ) {
135  # Default language in which the diff text is written.
136  $this->mDiffLang = $this->getTitle()->getPageLanguage();
137  }
138 
139  return $this->mDiffLang;
140  }
141 
145  public function wasCacheHit() {
146  return $this->mCacheHit;
147  }
148 
152  public function getOldid() {
153  $this->loadRevisionIds();
154 
155  return $this->mOldid;
156  }
157 
161  public function getNewid() {
162  $this->loadRevisionIds();
163 
164  return $this->mNewid;
165  }
166 
175  public function deletedLink( $id ) {
176  if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) {
177  $dbr = wfGetDB( DB_SLAVE );
178  $row = $dbr->selectRow( 'archive', '*',
179  array( 'ar_rev_id' => $id ),
180  __METHOD__ );
181  if ( $row ) {
183  $title = Title::makeTitleSafe( $row->ar_namespace, $row->ar_title );
184 
185  return SpecialPage::getTitleFor( 'Undelete' )->getFullURL( array(
186  'target' => $title->getPrefixedText(),
187  'timestamp' => $rev->getTimestamp()
188  ) );
189  }
190  }
191 
192  return false;
193  }
194 
202  public function deletedIdMarker( $id ) {
203  $link = $this->deletedLink( $id );
204  if ( $link ) {
205  return "[$link $id]";
206  } else {
207  return $id;
208  }
209  }
210 
211  private function showMissingRevision() {
212  $out = $this->getOutput();
213 
214  $missing = array();
215  if ( $this->mOldRev === null ||
216  ( $this->mOldRev && $this->mOldContent === null )
217  ) {
218  $missing[] = $this->deletedIdMarker( $this->mOldid );
219  }
220  if ( $this->mNewRev === null ||
221  ( $this->mNewRev && $this->mNewContent === null )
222  ) {
223  $missing[] = $this->deletedIdMarker( $this->mNewid );
224  }
225 
226  $out->setPageTitle( $this->msg( 'errorpagetitle' ) );
227  $out->addWikiMsg( 'difference-missing-revision',
228  $this->getLanguage()->listToText( $missing ), count( $missing ) );
229  }
230 
231  public function showDiffPage( $diffOnly = false ) {
232  wfProfileIn( __METHOD__ );
233 
234  # Allow frames except in certain special cases
235  $out = $this->getOutput();
236  $out->allowClickjacking();
237  $out->setRobotPolicy( 'noindex,nofollow' );
238 
239  if ( !$this->loadRevisionData() ) {
240  $this->showMissingRevision();
241  wfProfileOut( __METHOD__ );
242 
243  return;
244  }
245 
246  $user = $this->getUser();
247  $permErrors = $this->mNewPage->getUserPermissionsErrors( 'read', $user );
248  if ( $this->mOldPage ) { # mOldPage might not be set, see below.
249  $permErrors = wfMergeErrorArrays( $permErrors,
250  $this->mOldPage->getUserPermissionsErrors( 'read', $user ) );
251  }
252  if ( count( $permErrors ) ) {
253  wfProfileOut( __METHOD__ );
254  throw new PermissionsError( 'read', $permErrors );
255  }
256 
257  $rollback = '';
258 
259  $query = array();
260  # Carry over 'diffonly' param via navigation links
261  if ( $diffOnly != $user->getBoolOption( 'diffonly' ) ) {
262  $query['diffonly'] = $diffOnly;
263  }
264  # Cascade unhide param in links for easy deletion browsing
265  if ( $this->unhide ) {
266  $query['unhide'] = 1;
267  }
268 
269  # Check if one of the revisions is deleted/suppressed
270  $deleted = $suppressed = false;
271  $allowed = $this->mNewRev->userCan( Revision::DELETED_TEXT, $user );
272 
273  $revisionTools = array();
274 
275  # mOldRev is false if the difference engine is called with a "vague" query for
276  # a diff between a version V and its previous version V' AND the version V
277  # is the first version of that article. In that case, V' does not exist.
278  if ( $this->mOldRev === false ) {
279  $out->setPageTitle( $this->msg( 'difference-title', $this->mNewPage->getPrefixedText() ) );
280  $samePage = true;
281  $oldHeader = '';
282  } else {
283  wfRunHooks( 'DiffViewHeader', array( $this, $this->mOldRev, $this->mNewRev ) );
284 
285  if ( $this->mNewPage->equals( $this->mOldPage ) ) {
286  $out->setPageTitle( $this->msg( 'difference-title', $this->mNewPage->getPrefixedText() ) );
287  $samePage = true;
288  } else {
289  $out->setPageTitle( $this->msg( 'difference-title-multipage',
290  $this->mOldPage->getPrefixedText(), $this->mNewPage->getPrefixedText() ) );
291  $out->addSubtitle( $this->msg( 'difference-multipage' ) );
292  $samePage = false;
293  }
294 
295  if ( $samePage && $this->mNewPage->quickUserCan( 'edit', $user ) ) {
296  if ( $this->mNewRev->isCurrent() && $this->mNewPage->userCan( 'rollback', $user ) ) {
297  $rollbackLink = Linker::generateRollback( $this->mNewRev, $this->getContext() );
298  if ( $rollbackLink ) {
299  $out->preventClickjacking();
300  $rollback = '&#160;&#160;&#160;' . $rollbackLink;
301  }
302  }
303 
304  if ( !$this->mOldRev->isDeleted( Revision::DELETED_TEXT ) &&
305  !$this->mNewRev->isDeleted( Revision::DELETED_TEXT )
306  ) {
307  $undoLink = Html::element( 'a', array(
308  'href' => $this->mNewPage->getLocalURL( array(
309  'action' => 'edit',
310  'undoafter' => $this->mOldid,
311  'undo' => $this->mNewid
312  ) ),
313  'title' => Linker::titleAttrib( 'undo' )
314  ),
315  $this->msg( 'editundo' )->text()
316  );
317  $revisionTools[] = $undoLink;
318  }
319  }
320 
321  # Make "previous revision link"
322  if ( $samePage && $this->mOldRev->getPrevious() ) {
323  $prevlink = Linker::linkKnown(
324  $this->mOldPage,
325  $this->msg( 'previousdiff' )->escaped(),
326  array( 'id' => 'differences-prevlink' ),
327  array( 'diff' => 'prev', 'oldid' => $this->mOldid ) + $query
328  );
329  } else {
330  $prevlink = '&#160;';
331  }
332 
333  if ( $this->mOldRev->isMinor() ) {
334  $oldminor = ChangesList::flag( 'minor' );
335  } else {
336  $oldminor = '';
337  }
338 
339  $ldel = $this->revisionDeleteLink( $this->mOldRev );
340  $oldRevisionHeader = $this->getRevisionHeader( $this->mOldRev, 'complete' );
341  $oldChangeTags = ChangeTags::formatSummaryRow( $this->mOldTags, 'diff' );
342 
343  $oldHeader = '<div id="mw-diff-otitle1"><strong>' . $oldRevisionHeader . '</strong></div>' .
344  '<div id="mw-diff-otitle2">' .
345  Linker::revUserTools( $this->mOldRev, !$this->unhide ) . '</div>' .
346  '<div id="mw-diff-otitle3">' . $oldminor .
347  Linker::revComment( $this->mOldRev, !$diffOnly, !$this->unhide ) . $ldel . '</div>' .
348  '<div id="mw-diff-otitle5">' . $oldChangeTags[0] . '</div>' .
349  '<div id="mw-diff-otitle4">' . $prevlink . '</div>';
350 
351  if ( $this->mOldRev->isDeleted( Revision::DELETED_TEXT ) ) {
352  $deleted = true; // old revisions text is hidden
353  if ( $this->mOldRev->isDeleted( Revision::DELETED_RESTRICTED ) ) {
354  $suppressed = true; // also suppressed
355  }
356  }
357 
358  # Check if this user can see the revisions
359  if ( !$this->mOldRev->userCan( Revision::DELETED_TEXT, $user ) ) {
360  $allowed = false;
361  }
362  }
363 
364  # Make "next revision link"
365  # Skip next link on the top revision
366  if ( $samePage && !$this->mNewRev->isCurrent() ) {
367  $nextlink = Linker::linkKnown(
368  $this->mNewPage,
369  $this->msg( 'nextdiff' )->escaped(),
370  array( 'id' => 'differences-nextlink' ),
371  array( 'diff' => 'next', 'oldid' => $this->mNewid ) + $query
372  );
373  } else {
374  $nextlink = '&#160;';
375  }
376 
377  if ( $this->mNewRev->isMinor() ) {
378  $newminor = ChangesList::flag( 'minor' );
379  } else {
380  $newminor = '';
381  }
382 
383  # Handle RevisionDelete links...
384  $rdel = $this->revisionDeleteLink( $this->mNewRev );
385 
386  # Allow extensions to define their own revision tools
387  wfRunHooks( 'DiffRevisionTools', array( $this->mNewRev, &$revisionTools, $this->mOldRev ) );
388  $formattedRevisionTools = array();
389  // Put each one in parentheses (poor man's button)
390  foreach ( $revisionTools as $tool ) {
391  $formattedRevisionTools[] = $this->msg( 'parentheses' )->rawParams( $tool )->escaped();
392  }
393  $newRevisionHeader = $this->getRevisionHeader( $this->mNewRev, 'complete' ) .
394  ' ' . implode( ' ', $formattedRevisionTools );
395  $newChangeTags = ChangeTags::formatSummaryRow( $this->mNewTags, 'diff' );
396 
397  $newHeader = '<div id="mw-diff-ntitle1"><strong>' . $newRevisionHeader . '</strong></div>' .
398  '<div id="mw-diff-ntitle2">' . Linker::revUserTools( $this->mNewRev, !$this->unhide ) .
399  " $rollback</div>" .
400  '<div id="mw-diff-ntitle3">' . $newminor .
401  Linker::revComment( $this->mNewRev, !$diffOnly, !$this->unhide ) . $rdel . '</div>' .
402  '<div id="mw-diff-ntitle5">' . $newChangeTags[0] . '</div>' .
403  '<div id="mw-diff-ntitle4">' . $nextlink . $this->markPatrolledLink() . '</div>';
404 
405  if ( $this->mNewRev->isDeleted( Revision::DELETED_TEXT ) ) {
406  $deleted = true; // new revisions text is hidden
407  if ( $this->mNewRev->isDeleted( Revision::DELETED_RESTRICTED ) ) {
408  $suppressed = true; // also suppressed
409  }
410  }
411 
412  # If the diff cannot be shown due to a deleted revision, then output
413  # the diff header and links to unhide (if available)...
414  if ( $deleted && ( !$this->unhide || !$allowed ) ) {
415  $this->showDiffStyle();
416  $multi = $this->getMultiNotice();
417  $out->addHTML( $this->addHeader( '', $oldHeader, $newHeader, $multi ) );
418  if ( !$allowed ) {
419  $msg = $suppressed ? 'rev-suppressed-no-diff' : 'rev-deleted-no-diff';
420  # Give explanation for why revision is not visible
421  $out->wrapWikiMsg( "<div id='mw-$msg' class='mw-warning plainlinks'>\n$1\n</div>\n",
422  array( $msg ) );
423  } else {
424  # Give explanation and add a link to view the diff...
425  $query = $this->getRequest()->appendQueryValue( 'unhide', '1', true );
426  $link = $this->getTitle()->getFullURL( $query );
427  $msg = $suppressed ? 'rev-suppressed-unhide-diff' : 'rev-deleted-unhide-diff';
428  $out->wrapWikiMsg(
429  "<div id='mw-$msg' class='mw-warning plainlinks'>\n$1\n</div>\n",
430  array( $msg, $link )
431  );
432  }
433  # Otherwise, output a regular diff...
434  } else {
435  # Add deletion notice if the user is viewing deleted content
436  $notice = '';
437  if ( $deleted ) {
438  $msg = $suppressed ? 'rev-suppressed-diff-view' : 'rev-deleted-diff-view';
439  $notice = "<div id='mw-$msg' class='mw-warning plainlinks'>\n" .
440  $this->msg( $msg )->parse() .
441  "</div>\n";
442  }
443  $this->showDiff( $oldHeader, $newHeader, $notice );
444  if ( !$diffOnly ) {
445  $this->renderNewRevision();
446  }
447  }
448  wfProfileOut( __METHOD__ );
449  }
450 
459  protected function markPatrolledLink() {
460  global $wgUseRCPatrol, $wgEnableAPI, $wgEnableWriteAPI;
461  $user = $this->getUser();
462 
463  if ( $this->mMarkPatrolledLink === null ) {
464  // Prepare a change patrol link, if applicable
465  if (
466  // Is patrolling enabled and the user allowed to?
467  $wgUseRCPatrol && $this->mNewPage->quickUserCan( 'patrol', $user ) &&
468  // Only do this if the revision isn't more than 6 hours older
469  // than the Max RC age (6h because the RC might not be cleaned out regularly)
470  RecentChange::isInRCLifespan( $this->mNewRev->getTimestamp(), 21600 )
471  ) {
472  // Look for an unpatrolled change corresponding to this diff
473 
474  $db = wfGetDB( DB_SLAVE );
475  $change = RecentChange::newFromConds(
476  array(
477  'rc_timestamp' => $db->timestamp( $this->mNewRev->getTimestamp() ),
478  'rc_this_oldid' => $this->mNewid,
479  'rc_patrolled' => 0
480  ),
481  __METHOD__,
482  array( 'USE INDEX' => 'rc_timestamp' )
483  );
484 
485  if ( $change && $change->getPerformer()->getName() !== $user->getName() ) {
486  $rcid = $change->getAttribute( 'rc_id' );
487  } else {
488  // None found or the page has been created by the current user.
489  // If the user could patrol this it already would be patrolled
490  $rcid = 0;
491  }
492  // Build the link
493  if ( $rcid ) {
494  $this->getOutput()->preventClickjacking();
495  if ( $wgEnableAPI && $wgEnableWriteAPI
496  && $user->isAllowed( 'writeapi' )
497  ) {
498  $this->getOutput()->addModules( 'mediawiki.page.patrol.ajax' );
499  }
500 
501  $token = $user->getEditToken( $rcid );
502  $this->mMarkPatrolledLink = ' <span class="patrollink">[' . Linker::linkKnown(
503  $this->mNewPage,
504  $this->msg( 'markaspatrolleddiff' )->escaped(),
505  array( 'class' => 'mw-patrollink' ),
506  array(
507  'action' => 'markpatrolled',
508  'rcid' => $rcid,
509  'token' => $token,
510  )
511  ) . ']</span>';
512  } else {
513  $this->mMarkPatrolledLink = '';
514  }
515  } else {
516  $this->mMarkPatrolledLink = '';
517  }
518  }
519 
521  }
522 
528  protected function revisionDeleteLink( $rev ) {
529  $link = Linker::getRevDeleteLink( $this->getUser(), $rev, $rev->getTitle() );
530  if ( $link !== '' ) {
531  $link = '&#160;&#160;&#160;' . $link . ' ';
532  }
533 
534  return $link;
535  }
536 
540  public function renderNewRevision() {
541  wfProfileIn( __METHOD__ );
542  $out = $this->getOutput();
543  $revHeader = $this->getRevisionHeader( $this->mNewRev );
544  # Add "current version as of X" title
545  $out->addHTML( "<hr class='diff-hr' />
546  <h2 class='diff-currentversion-title'>{$revHeader}</h2>\n" );
547  # Page content may be handled by a hooked call instead...
548  # @codingStandardsIgnoreStart Ignoring long lines.
549  if ( wfRunHooks( 'ArticleContentOnDiff', array( $this, $out ) ) ) {
550  $this->loadNewText();
551  $out->setRevisionId( $this->mNewid );
552  $out->setRevisionTimestamp( $this->mNewRev->getTimestamp() );
553  $out->setArticleFlag( true );
554 
555  // NOTE: only needed for B/C: custom rendering of JS/CSS via hook
556  if ( $this->mNewPage->isCssJsSubpage() || $this->mNewPage->isCssOrJsPage() ) {
557  // Stolen from Article::view --AG 2007-10-11
558  // Give hooks a chance to customise the output
559  // @todo standardize this crap into one function
560  if ( ContentHandler::runLegacyHooks( 'ShowRawCssJs', array( $this->mNewContent, $this->mNewPage, $out ) ) ) {
561  // NOTE: deprecated hook, B/C only
562  // use the content object's own rendering
563  $cnt = $this->mNewRev->getContent();
564  $po = $cnt ? $cnt->getParserOutput( $this->mNewRev->getTitle(), $this->mNewRev->getId() ) : null;
565  $txt = $po ? $po->getText() : '';
566  $out->addHTML( $txt );
567  }
568  } elseif ( !wfRunHooks( 'ArticleContentViewCustom', array( $this->mNewContent, $this->mNewPage, $out ) ) ) {
569  // Handled by extension
570  } elseif ( !ContentHandler::runLegacyHooks( 'ArticleViewCustom', array( $this->mNewContent, $this->mNewPage, $out ) ) ) {
571  // NOTE: deprecated hook, B/C only
572  // Handled by extension
573  } else {
574  // Normal page
575  if ( $this->getTitle()->equals( $this->mNewPage ) ) {
576  // If the Title stored in the context is the same as the one
577  // of the new revision, we can use its associated WikiPage
578  // object.
579  $wikiPage = $this->getWikiPage();
580  } else {
581  // Otherwise we need to create our own WikiPage object
582  $wikiPage = WikiPage::factory( $this->mNewPage );
583  }
584 
585  $parserOutput = $this->getParserOutput( $wikiPage, $this->mNewRev );
586 
587  # Also try to load it as a redirect
588  $rt = $this->mNewContent ? $this->mNewContent->getRedirectTarget() : null;
589 
590  if ( $rt ) {
591  $article = Article::newFromTitle( $this->mNewPage, $this->getContext() );
592  $out->addHTML( $article->viewRedirect( $rt ) );
593 
594  # WikiPage::getParserOutput() should not return false, but just in case
595  if ( $parserOutput ) {
596  # Show categories etc.
597  $out->addParserOutputNoText( $parserOutput );
598  }
599  } elseif ( $parserOutput ) {
600  $out->addParserOutput( $parserOutput );
601  }
602  }
603  }
604  # @codingStandardsIgnoreEnd
605 
606  # Add redundant patrol link on bottom...
607  $out->addHTML( $this->markPatrolledLink() );
608 
609  wfProfileOut( __METHOD__ );
610  }
611 
612  protected function getParserOutput( WikiPage $page, Revision $rev ) {
613  $parserOptions = $page->makeParserOptions( $this->getContext() );
614 
615  if ( !$rev->isCurrent() || !$rev->getTitle()->quickUserCan( "edit" ) ) {
616  $parserOptions->setEditSection( false );
617  }
618 
619  $parserOutput = $page->getParserOutput( $parserOptions, $rev->getId() );
620 
621  return $parserOutput;
622  }
623 
634  public function showDiff( $otitle, $ntitle, $notice = '' ) {
635  $diff = $this->getDiff( $otitle, $ntitle, $notice );
636  if ( $diff === false ) {
637  $this->showMissingRevision();
638 
639  return false;
640  } else {
641  $this->showDiffStyle();
642  $this->getOutput()->addHTML( $diff );
643 
644  return true;
645  }
646  }
647 
651  public function showDiffStyle() {
652  $this->getOutput()->addModuleStyles( 'mediawiki.action.history.diff' );
653  }
654 
664  public function getDiff( $otitle, $ntitle, $notice = '' ) {
665  $body = $this->getDiffBody();
666  if ( $body === false ) {
667  return false;
668  }
669 
670  $multi = $this->getMultiNotice();
671  // Display a message when the diff is empty
672  if ( $body === '' ) {
673  $notice .= '<div class="mw-diff-empty">' .
674  $this->msg( 'diff-empty' )->parse() .
675  "</div>\n";
676  }
677 
678  return $this->addHeader( $body, $otitle, $ntitle, $multi, $notice );
679  }
680 
686  public function getDiffBody() {
687  global $wgMemc;
688  wfProfileIn( __METHOD__ );
689  $this->mCacheHit = true;
690  // Check if the diff should be hidden from this user
691  if ( !$this->loadRevisionData() ) {
692  wfProfileOut( __METHOD__ );
693 
694  return false;
695  } elseif ( $this->mOldRev &&
696  !$this->mOldRev->userCan( Revision::DELETED_TEXT, $this->getUser() )
697  ) {
698  wfProfileOut( __METHOD__ );
699 
700  return false;
701  } elseif ( $this->mNewRev &&
702  !$this->mNewRev->userCan( Revision::DELETED_TEXT, $this->getUser() )
703  ) {
704  wfProfileOut( __METHOD__ );
705 
706  return false;
707  }
708  // Short-circuit
709  if ( $this->mOldRev === false || ( $this->mOldRev && $this->mNewRev
710  && $this->mOldRev->getID() == $this->mNewRev->getID() )
711  ) {
712  wfProfileOut( __METHOD__ );
713 
714  return '';
715  }
716  // Cacheable?
717  $key = false;
718  if ( $this->mOldid && $this->mNewid ) {
719  $key = $this->getDiffBodyCacheKey();
720 
721  // Try cache
722  if ( !$this->mRefreshCache ) {
723  $difftext = $wgMemc->get( $key );
724  if ( $difftext ) {
725  wfIncrStats( 'diff_cache_hit' );
726  $difftext = $this->localiseLineNumbers( $difftext );
727  $difftext .= "\n<!-- diff cache key $key -->\n";
728  wfProfileOut( __METHOD__ );
729 
730  return $difftext;
731  }
732  } // don't try to load but save the result
733  }
734  $this->mCacheHit = false;
735 
736  // Loadtext is permission safe, this just clears out the diff
737  if ( !$this->loadText() ) {
738  wfProfileOut( __METHOD__ );
739 
740  return false;
741  }
742 
743  $difftext = $this->generateContentDiffBody( $this->mOldContent, $this->mNewContent );
744 
745  // Save to cache for 7 days
746  if ( !wfRunHooks( 'AbortDiffCache', array( &$this ) ) ) {
747  wfIncrStats( 'diff_uncacheable' );
748  } elseif ( $key !== false && $difftext !== false ) {
749  wfIncrStats( 'diff_cache_miss' );
750  $wgMemc->set( $key, $difftext, 7 * 86400 );
751  } else {
752  wfIncrStats( 'diff_uncacheable' );
753  }
754  // Replace line numbers with the text in the user's language
755  if ( $difftext !== false ) {
756  $difftext = $this->localiseLineNumbers( $difftext );
757  }
758  wfProfileOut( __METHOD__ );
759 
760  return $difftext;
761  }
762 
771  protected function getDiffBodyCacheKey() {
772  if ( !$this->mOldid || !$this->mNewid ) {
773  throw new MWException( 'mOldid and mNewid must be set to get diff cache key.' );
774  }
775 
776  return wfMemcKey( 'diff', 'version', MW_DIFF_VERSION,
777  'oldid', $this->mOldid, 'newid', $this->mNewid );
778  }
779 
799  public function generateContentDiffBody( Content $old, Content $new ) {
800  if ( !( $old instanceof TextContent ) ) {
801  throw new MWException( "Diff not implemented for " . get_class( $old ) . "; " .
802  "override generateContentDiffBody to fix this." );
803  }
804 
805  if ( !( $new instanceof TextContent ) ) {
806  throw new MWException( "Diff not implemented for " . get_class( $new ) . "; "
807  . "override generateContentDiffBody to fix this." );
808  }
809 
810  $otext = $old->serialize();
811  $ntext = $new->serialize();
812 
813  return $this->generateTextDiffBody( $otext, $ntext );
814  }
815 
825  public function generateDiffBody( $otext, $ntext ) {
826  ContentHandler::deprecated( __METHOD__, "1.21" );
827 
828  return $this->generateTextDiffBody( $otext, $ntext );
829  }
830 
841  public function generateTextDiffBody( $otext, $ntext ) {
842  $self = $this;
843  $diff = function() use ( $self, $otext, $ntext ) {
844  return $self->textDiff( $otext, $ntext );
845  };
846 
847  $error = function( $status ) {
848  throw new FatalError( $status->getWikiText() );
849  };
850 
851  // Use PoolCounter if the diff looks like it can be expensive
852  if ( strlen( $otext ) + strlen( $ntext ) > 20000 ) {
853  $work = new PoolCounterWorkViaCallback( 'diff',
854  md5( $otext ) . md5( $ntext ),
855  array( 'doWork' => $diff, 'error' => $error )
856  );
857  return $work->execute();
858  }
859 
860  return $diff();
861  }
862 
870  public function textDiff( $otext, $ntext ) {
871  global $wgExternalDiffEngine, $wgContLang;
872 
873  wfProfileIn( __METHOD__ );
874 
875  $otext = str_replace( "\r\n", "\n", $otext );
876  $ntext = str_replace( "\r\n", "\n", $ntext );
877 
878  if ( $wgExternalDiffEngine == 'wikidiff' && function_exists( 'wikidiff_do_diff' ) ) {
879  # For historical reasons, external diff engine expects
880  # input text to be HTML-escaped already
881  $otext = htmlspecialchars( $wgContLang->segmentForDiff( $otext ) );
882  $ntext = htmlspecialchars( $wgContLang->segmentForDiff( $ntext ) );
883  wfProfileOut( __METHOD__ );
884 
885  return $wgContLang->unsegmentForDiff( wikidiff_do_diff( $otext, $ntext, 2 ) ) .
886  $this->debug( 'wikidiff1' );
887  }
888 
889  if ( $wgExternalDiffEngine == 'wikidiff2' && function_exists( 'wikidiff2_do_diff' ) ) {
890  # Better external diff engine, the 2 may some day be dropped
891  # This one does the escaping and segmenting itself
892  wfProfileIn( 'wikidiff2_do_diff' );
893  $text = wikidiff2_do_diff( $otext, $ntext, 2 );
894  $text .= $this->debug( 'wikidiff2' );
895  wfProfileOut( 'wikidiff2_do_diff' );
896  wfProfileOut( __METHOD__ );
897 
898  return $text;
899  }
900  if ( $wgExternalDiffEngine != 'wikidiff3' && $wgExternalDiffEngine !== false ) {
901  # Diff via the shell
902  $tmpDir = wfTempDir();
903  $tempName1 = tempnam( $tmpDir, 'diff_' );
904  $tempName2 = tempnam( $tmpDir, 'diff_' );
905 
906  $tempFile1 = fopen( $tempName1, "w" );
907  if ( !$tempFile1 ) {
908  wfProfileOut( __METHOD__ );
909 
910  return false;
911  }
912  $tempFile2 = fopen( $tempName2, "w" );
913  if ( !$tempFile2 ) {
914  wfProfileOut( __METHOD__ );
915 
916  return false;
917  }
918  fwrite( $tempFile1, $otext );
919  fwrite( $tempFile2, $ntext );
920  fclose( $tempFile1 );
921  fclose( $tempFile2 );
922  $cmd = wfEscapeShellArg( $wgExternalDiffEngine, $tempName1, $tempName2 );
923  wfProfileIn( __METHOD__ . "-shellexec" );
924  $difftext = wfShellExec( $cmd );
925  $difftext .= $this->debug( "external $wgExternalDiffEngine" );
926  wfProfileOut( __METHOD__ . "-shellexec" );
927  unlink( $tempName1 );
928  unlink( $tempName2 );
929  wfProfileOut( __METHOD__ );
930 
931  return $difftext;
932  }
933 
934  # Native PHP diff
935  $ota = explode( "\n", $wgContLang->segmentForDiff( $otext ) );
936  $nta = explode( "\n", $wgContLang->segmentForDiff( $ntext ) );
937  $diffs = new Diff( $ota, $nta );
938  $formatter = new TableDiffFormatter();
939  $difftext = $wgContLang->unsegmentForDiff( $formatter->format( $diffs ) ) .
940  wfProfileOut( __METHOD__ );
941 
942  return $difftext;
943  }
944 
953  protected function debug( $generator = "internal" ) {
954  global $wgShowHostnames;
955  if ( !$this->enableDebugComment ) {
956  return '';
957  }
958  $data = array( $generator );
959  if ( $wgShowHostnames ) {
960  $data[] = wfHostname();
961  }
962  $data[] = wfTimestamp( TS_DB );
963 
964  return "<!-- diff generator: " .
965  implode( " ", array_map( "htmlspecialchars", $data ) ) .
966  " -->\n";
967  }
968 
976  public function localiseLineNumbers( $text ) {
977  return preg_replace_callback(
978  '/<!--LINE (\d+)-->/',
979  array( &$this, 'localiseLineNumbersCb' ),
980  $text
981  );
982  }
983 
984  public function localiseLineNumbersCb( $matches ) {
985  if ( $matches[1] === '1' && $this->mReducedLineNumbers ) {
986  return '';
987  }
988 
989  return $this->msg( 'lineno' )->numParams( $matches[1] )->escaped();
990  }
991 
997  public function getMultiNotice() {
998  if ( !is_object( $this->mOldRev ) || !is_object( $this->mNewRev ) ) {
999  return '';
1000  } elseif ( !$this->mOldPage->equals( $this->mNewPage ) ) {
1001  // Comparing two different pages? Count would be meaningless.
1002  return '';
1003  }
1004 
1005  if ( $this->mOldRev->getTimestamp() > $this->mNewRev->getTimestamp() ) {
1006  $oldRev = $this->mNewRev; // flip
1007  $newRev = $this->mOldRev; // flip
1008  } else { // normal case
1009  $oldRev = $this->mOldRev;
1010  $newRev = $this->mNewRev;
1011  }
1012 
1013  // Sanity: don't show the notice if too many rows must be scanned
1014  // @TODO: show some special message for that case
1015  $nEdits = $this->mNewPage->countRevisionsBetween( $oldRev, $newRev, 1000 );
1016  if ( $nEdits > 0 && $nEdits <= 1000 ) {
1017  $limit = 100; // use diff-multi-manyusers if too many users
1018  $users = $this->mNewPage->getAuthorsBetween( $oldRev, $newRev, $limit );
1019  $numUsers = count( $users );
1020 
1021  if ( $numUsers == 1 && $users[0] == $newRev->getRawUserText() ) {
1022  $numUsers = 0; // special case to say "by the same user" instead of "by one other user"
1023  }
1025  return self::intermediateEditsMsg( $nEdits, $numUsers, $limit );
1026  }
1027 
1028  return ''; // nothing
1029  }
1030 
1040  public static function intermediateEditsMsg( $numEdits, $numUsers, $limit ) {
1041  if ( $numUsers === 0 ) {
1042  $msg = 'diff-multi-sameuser';
1043  } elseif ( $numUsers > $limit ) {
1044  $msg = 'diff-multi-manyusers';
1045  $numUsers = $limit;
1046  } else {
1047  $msg = 'diff-multi-otherusers';
1048  }
1049 
1050  return wfMessage( $msg )->numParams( $numEdits, $numUsers )->parse();
1051  }
1052 
1062  protected function getRevisionHeader( Revision $rev, $complete = '' ) {
1063  $lang = $this->getLanguage();
1064  $user = $this->getUser();
1065  $revtimestamp = $rev->getTimestamp();
1066  $timestamp = $lang->userTimeAndDate( $revtimestamp, $user );
1067  $dateofrev = $lang->userDate( $revtimestamp, $user );
1068  $timeofrev = $lang->userTime( $revtimestamp, $user );
1069 
1070  $header = $this->msg(
1071  $rev->isCurrent() ? 'currentrev-asof' : 'revisionasof',
1072  $timestamp,
1073  $dateofrev,
1074  $timeofrev
1075  )->escaped();
1076 
1077  if ( $complete !== 'complete' ) {
1078  return $header;
1079  }
1080 
1081  $title = $rev->getTitle();
1082 
1083  $header = Linker::linkKnown( $title, $header, array(),
1084  array( 'oldid' => $rev->getID() ) );
1085 
1086  if ( $rev->userCan( Revision::DELETED_TEXT, $user ) ) {
1087  $editQuery = array( 'action' => 'edit' );
1088  if ( !$rev->isCurrent() ) {
1089  $editQuery['oldid'] = $rev->getID();
1090  }
1091 
1092  $key = $title->quickUserCan( 'edit', $user ) ? 'editold' : 'viewsourceold';
1093  $msg = $this->msg( $key )->escaped();
1094  $header .= ' ' . $this->msg( 'parentheses' )->rawParams(
1095  Linker::linkKnown( $title, $msg, array(), $editQuery ) )->plain();
1096  if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
1097  $header = Html::rawElement(
1098  'span',
1099  array( 'class' => 'history-deleted' ),
1100  $header
1101  );
1102  }
1103  } else {
1104  $header = Html::rawElement( 'span', array( 'class' => 'history-deleted' ), $header );
1105  }
1107  return $header;
1108  }
1109 
1122  public function addHeader( $diff, $otitle, $ntitle, $multi = '', $notice = '' ) {
1123  // shared.css sets diff in interface language/dir, but the actual content
1124  // is often in a different language, mostly the page content language/dir
1125  $tableClass = 'diff diff-contentalign-' . htmlspecialchars( $this->getDiffLang()->alignStart() );
1126  $header = "<table class='$tableClass'>";
1127 
1128  if ( !$diff && !$otitle ) {
1129  $header .= "
1130  <tr style='vertical-align: top;'>
1131  <td class='diff-ntitle'>{$ntitle}</td>
1132  </tr>";
1133  $multiColspan = 1;
1134  } else {
1135  if ( $diff ) { // Safari/Chrome show broken output if cols not used
1136  $header .= "
1137  <col class='diff-marker' />
1138  <col class='diff-content' />
1139  <col class='diff-marker' />
1140  <col class='diff-content' />";
1141  $colspan = 2;
1142  $multiColspan = 4;
1143  } else {
1144  $colspan = 1;
1145  $multiColspan = 2;
1146  }
1147  if ( $otitle || $ntitle ) {
1148  $header .= "
1149  <tr style='vertical-align: top;'>
1150  <td colspan='$colspan' class='diff-otitle'>{$otitle}</td>
1151  <td colspan='$colspan' class='diff-ntitle'>{$ntitle}</td>
1152  </tr>";
1153  }
1154  }
1156  if ( $multi != '' ) {
1157  $header .= "<tr><td colspan='{$multiColspan}' style='text-align: center;' " .
1158  "class='diff-multi'>{$multi}</td></tr>";
1159  }
1160  if ( $notice != '' ) {
1161  $header .= "<tr><td colspan='{$multiColspan}' style='text-align: center;'>{$notice}</td></tr>";
1162  }
1163 
1164  return $header . $diff . "</table>";
1165  }
1166 
1171  public function setText( $oldText, $newText ) {
1172  ContentHandler::deprecated( __METHOD__, "1.21" );
1173 
1174  $oldContent = ContentHandler::makeContent( $oldText, $this->getTitle() );
1175  $newContent = ContentHandler::makeContent( $newText, $this->getTitle() );
1176 
1177  $this->setContent( $oldContent, $newContent );
1178  }
1179 
1184  public function setContent( Content $oldContent, Content $newContent ) {
1185  $this->mOldContent = $oldContent;
1186  $this->mNewContent = $newContent;
1187 
1188  $this->mTextLoaded = 2;
1189  $this->mRevisionsLoaded = true;
1190  }
1191 
1197  public function setTextLanguage( $lang ) {
1198  $this->mDiffLang = wfGetLangObj( $lang );
1199  }
1200 
1212  public function mapDiffPrevNext( $old, $new ) {
1213  if ( $new === 'prev' ) {
1214  // Show diff between revision $old and the previous one. Get previous one from DB.
1215  $newid = intval( $old );
1216  $oldid = $this->getTitle()->getPreviousRevisionID( $newid );
1217  } elseif ( $new === 'next' ) {
1218  // Show diff between revision $old and the next one. Get next one from DB.
1219  $oldid = intval( $old );
1220  $newid = $this->getTitle()->getNextRevisionID( $oldid );
1221  } else {
1222  $oldid = intval( $old );
1223  $newid = intval( $new );
1224  }
1225 
1226  return array( $oldid, $newid );
1227  }
1228 
1232  private function loadRevisionIds() {
1233  if ( $this->mRevisionsIdsLoaded ) {
1234  return;
1235  }
1236 
1237  $this->mRevisionsIdsLoaded = true;
1238 
1239  $old = $this->mOldid;
1240  $new = $this->mNewid;
1241 
1242  list( $this->mOldid, $this->mNewid ) = self::mapDiffPrevNext( $old, $new );
1243  if ( $new === 'next' && $this->mNewid === false ) {
1244  # if no result, NewId points to the newest old revision. The only newer
1245  # revision is cur, which is "0".
1246  $this->mNewid = 0;
1247  }
1248 
1249  wfRunHooks(
1250  'NewDifferenceEngine',
1251  array( $this->getTitle(), &$this->mOldid, &$this->mNewid, $old, $new )
1252  );
1253  }
1254 
1267  public function loadRevisionData() {
1268  if ( $this->mRevisionsLoaded ) {
1269  return true;
1270  }
1271 
1272  // Whether it succeeds or fails, we don't want to try again
1273  $this->mRevisionsLoaded = true;
1274 
1275  $this->loadRevisionIds();
1276 
1277  // Load the new revision object
1278  if ( $this->mNewid ) {
1279  $this->mNewRev = Revision::newFromId( $this->mNewid );
1280  } else {
1281  $this->mNewRev = Revision::newFromTitle(
1282  $this->getTitle(),
1283  false,
1285  );
1286  }
1287 
1288  if ( !$this->mNewRev instanceof Revision ) {
1289  return false;
1290  }
1291 
1292  // Update the new revision ID in case it was 0 (makes life easier doing UI stuff)
1293  $this->mNewid = $this->mNewRev->getId();
1294  $this->mNewPage = $this->mNewRev->getTitle();
1295 
1296  // Load the old revision object
1297  $this->mOldRev = false;
1298  if ( $this->mOldid ) {
1299  $this->mOldRev = Revision::newFromId( $this->mOldid );
1300  } elseif ( $this->mOldid === 0 ) {
1301  $rev = $this->mNewRev->getPrevious();
1302  if ( $rev ) {
1303  $this->mOldid = $rev->getId();
1304  $this->mOldRev = $rev;
1305  } else {
1306  // No previous revision; mark to show as first-version only.
1307  $this->mOldid = false;
1308  $this->mOldRev = false;
1309  }
1310  } /* elseif ( $this->mOldid === false ) leave mOldRev false; */
1311 
1312  if ( is_null( $this->mOldRev ) ) {
1313  return false;
1314  }
1315 
1316  if ( $this->mOldRev ) {
1317  $this->mOldPage = $this->mOldRev->getTitle();
1318  }
1319 
1320  // Load tags information for both revisions
1321  $dbr = wfGetDB( DB_SLAVE );
1322  if ( $this->mOldid !== false ) {
1323  $this->mOldTags = $dbr->selectField(
1324  'tag_summary',
1325  'ts_tags',
1326  array( 'ts_rev_id' => $this->mOldid ),
1327  __METHOD__
1328  );
1329  } else {
1330  $this->mOldTags = false;
1331  }
1332  $this->mNewTags = $dbr->selectField(
1333  'tag_summary',
1334  'ts_tags',
1335  array( 'ts_rev_id' => $this->mNewid ),
1336  __METHOD__
1337  );
1338 
1339  return true;
1340  }
1341 
1347  public function loadText() {
1348  if ( $this->mTextLoaded == 2 ) {
1349  return true;
1350  }
1351 
1352  // Whether it succeeds or fails, we don't want to try again
1353  $this->mTextLoaded = 2;
1354 
1355  if ( !$this->loadRevisionData() ) {
1356  return false;
1357  }
1358 
1359  if ( $this->mOldRev ) {
1360  $this->mOldContent = $this->mOldRev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
1361  if ( $this->mOldContent === null ) {
1362  return false;
1363  }
1364  }
1366  if ( $this->mNewRev ) {
1367  $this->mNewContent = $this->mNewRev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
1368  if ( $this->mNewContent === null ) {
1369  return false;
1370  }
1371  }
1372 
1373  return true;
1374  }
1375 
1381  public function loadNewText() {
1382  if ( $this->mTextLoaded >= 1 ) {
1383  return true;
1384  }
1385 
1386  $this->mTextLoaded = 1;
1387 
1388  if ( !$this->loadRevisionData() ) {
1389  return false;
1390  }
1391 
1392  $this->mNewContent = $this->mNewRev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
1393 
1394  return true;
1395  }
1396 
1397 }
DifferenceEngine\$mRevisionsIdsLoaded
bool $mRevisionsIdsLoaded
Have the revisions IDs been loaded *.
Definition: DifferenceEngine.php:59
ContentHandler\deprecated
static deprecated( $func, $version, $component=false)
Logs a deprecation warning, visible if $wgDevelopmentWarnings, but only if self::$enableDeprecationWa...
Definition: ContentHandler.php:1030
DifferenceEngine\revisionDeleteLink
revisionDeleteLink( $rev)
Definition: DifferenceEngine.php:512
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:33
Revision\DELETED_RESTRICTED
const DELETED_RESTRICTED
Definition: Revision.php:68
DifferenceEngine\markPatrolledLink
markPatrolledLink()
Get a link to mark the change as patrolled, or '' if there's either no revision to patrol or the user...
Definition: DifferenceEngine.php:443
Linker\generateRollback
static generateRollback( $rev, IContextSource $context=null, $options=array( 'verify'))
Generate a rollback link for a given revision.
Definition: Linker.php:1779
ContextSource\getContext
getContext()
Get the RequestContext object.
Definition: ContextSource.php:40
wfShellExec
wfShellExec( $cmd, &$retval=null, $environ=array(), $limits=array(), $options=array())
Execute a shell command, with time and memory limits mirrored from the PHP configuration if supported...
Definition: GlobalFunctions.php:2851
DifferenceEngine\$mTextLoaded
int $mTextLoaded
How many text blobs have been loaded, 0, 1 or 2? *.
Definition: DifferenceEngine.php:63
Content\serialize
serialize( $format=null)
Convenience method for serializing this Content object.
wfMergeErrorArrays
wfMergeErrorArrays()
Merge arrays in the style of getUserPermissionsErrors, with duplicate removal e.g.
Definition: GlobalFunctions.php:260
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
Revision\newFromId
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:88
DifferenceEngine\$mNewContent
Content $mNewContent
Definition: DifferenceEngine.php:47
ContextSource\msg
msg()
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:175
DifferenceEngine\$mNewRev
Revision $mNewRev
Definition: DifferenceEngine.php:57
DifferenceEngine\$mOldPage
Title $mOldPage
Definition: DifferenceEngine.php:51
DifferenceEngine\$unhide
bool $unhide
Show rev_deleted content if allowed *.
Definition: DifferenceEngine.php:80
DifferenceEngine\$mOldTags
$mOldTags
Definition: DifferenceEngine.php:42
DifferenceEngine\setTextLanguage
setTextLanguage( $lang)
Set the language in which the diff text is written (Defaults to page content language).
Definition: DifferenceEngine.php:1181
DifferenceEngine\setReducedLineNumbers
setReducedLineNumbers( $value=true)
Definition: DifferenceEngine.php:110
$wgMemc
globals will be eliminated from MediaWiki replaced by an application object which would be passed to constructors Whether that would be an convenient solution remains to be but certainly PHP makes such object oriented programming models easier than they were in previous versions For the time being MediaWiki programmers will have to work in an environment with some global context At the time of globals were initialised on startup by MediaWiki of these were configuration which are documented in DefaultSettings php There is no comprehensive documentation for the remaining however some of the most important ones are listed below They are typically initialised either in index php or in Setup php For a description of the see design txt $wgTitle Title object created from the request URL $wgOut OutputPage object for HTTP response $wgUser User object for the user associated with the current request $wgLang Language object selected by user preferences $wgContLang Language object associated with the wiki being viewed $wgParser Parser object Parser extensions register their hooks here $wgRequest WebRequest to get request data $wgMemc
Definition: globals.txt:25
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3714
$timestamp
if( $limit) $timestamp
Definition: importImages.php:104
DifferenceEngine\setContent
setContent(Content $oldContent, Content $newContent)
Use specified text instead of loading from the database.
Definition: DifferenceEngine.php:1168
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:2530
DifferenceEngine\getNewid
getNewid()
Definition: DifferenceEngine.php:145
DifferenceEngine\getOldid
getOldid()
Definition: DifferenceEngine.php:136
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
DifferenceEngine\$mNewTags
$mNewTags
Definition: DifferenceEngine.php:43
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:37
RecentChange\newFromConds
static newFromConds( $conds, $fname=__METHOD__, $options=array())
Find the first recent change matching some specific conditions.
Definition: RecentChange.php:136
DifferenceEngine\$mRevisionsLoaded
bool $mRevisionsLoaded
Have the revisions been loaded *.
Definition: DifferenceEngine.php:61
DifferenceEngine\deletedIdMarker
deletedIdMarker( $id)
Build a wikitext link toward a deleted revision, if viewable.
Definition: DifferenceEngine.php:186
$limit
if( $sleep) $limit
Definition: importImages.php:99
WikiPage\makeParserOptions
makeParserOptions( $context)
Get parser options suitable for rendering the primary article wikitext.
Definition: WikiPage.php:2012
wfHostname
wfHostname()
Fetch server name for use in error reporting etc.
Definition: GlobalFunctions.php:1833
SpecialPage\getTitleFor
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name.
Definition: SpecialPage.php:74
TS_DB
const TS_DB
MySQL DATETIME (YYYY-MM-DD HH:MM:SS)
Definition: GlobalFunctions.php:2483
DifferenceEngine\$mNewPage
Title $mNewPage
Definition: DifferenceEngine.php:53
Revision\isCurrent
isCurrent()
Definition: Revision.php:1140
TableDiffFormatter
MediaWiki default table style diff formatter.
Definition: TableDiffFormatter.php:33
ContextSource\getRequest
getRequest()
Get the WebRequest object.
Definition: ContextSource.php:77
PermissionsError
Show an error when a user tries to do something they do not have the necessary permissions for.
Definition: PermissionsError.php:28
PoolCounterWorkViaCallback
Convenience class for dealing with PoolCounters using callbacks.
Definition: PoolCounterWork.php:155
$wgContLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the content language as $wgContLang
Definition: design.txt:56
DifferenceEngine\showDiffStyle
showDiffStyle()
Add style sheets and supporting JS for diff display.
Definition: DifferenceEngine.php:635
DifferenceEngine\loadNewText
loadNewText()
Load the text of the new revision, not the old one.
Definition: DifferenceEngine.php:1365
ContextSource\getUser
getUser()
Get the User object.
Definition: ContextSource.php:132
$link
set to $title object and return false for a match for latest after cache objects are set use the ContentHandler facility to handle CSS and JavaScript for highlighting & $link
Definition: hooks.txt:2160
ContextSource\getTitle
getTitle()
Get the Title object.
Definition: ContextSource.php:87
DifferenceEngine\showDiff
showDiff( $otitle, $ntitle, $notice='')
Get the diff text, send it to the OutputPage object Returns false if the diff could not be generated,...
Definition: DifferenceEngine.php:618
DifferenceEngine\getDiffBodyCacheKey
getDiffBodyCacheKey()
Returns the cache key for diff body text or content.
Definition: DifferenceEngine.php:755
Linker\linkKnown
static linkKnown( $target, $html=null, $customAttribs=array(), $query=array(), $options=array( 'known', 'noclasses'))
Identical to link(), except $options defaults to 'known'.
Definition: Linker.php:264
DifferenceEngine\$mOldid
int $mOldid
Definition: DifferenceEngine.php:38
ContentHandler\runLegacyHooks
static runLegacyHooks( $event, $args=array(), $warn=null)
Call a legacy hook that uses text instead of Content objects.
Definition: ContentHandler.php:1053
DifferenceEngine\$mNewid
int $mNewid
Definition: DifferenceEngine.php:40
ChangesList\flag
static flag( $flag)
Provide the "<abbr>" element appropriate to a given abbreviated flag, namely the flag indicating a ne...
Definition: ChangesList.php:132
DifferenceEngine\generateTextDiffBody
generateTextDiffBody( $otext, $ntext)
Generate a diff, no caching.
Definition: DifferenceEngine.php:825
$dbr
$dbr
Definition: testCompression.php:48
ContextSource\getLanguage
getLanguage()
Get the Language object.
Definition: ContextSource.php:154
below
controlled by $wgMainCacheType controlled by $wgParserCacheType controlled by $wgMessageCacheType If you set CACHE_NONE to one of the three control default value for MediaWiki still create a but requests to it are no ops and we always fall through to the database If the cache daemon can t be it should also disable itself fairly smoothly By $wgMemc is used but when it is $parserMemc or $messageMemc this is mentioned below
Definition: memcached.txt:96
Revision\FOR_THIS_USER
const FOR_THIS_USER
Definition: Revision.php:73
Revision
Definition: Revision.php:26
IDBAccessObject\READ_NORMAL
const READ_NORMAL
Definition: IDBAccessObject.php:53
DifferenceEngine\$mReducedLineNumbers
bool $mReducedLineNumbers
If true, line X is not displayed when X is 1, for example to increase readability and conserve space ...
Definition: DifferenceEngine.php:76
DifferenceEngine\loadRevisionData
loadRevisionData()
Load revision metadata for the specified articles.
Definition: DifferenceEngine.php:1251
DifferenceEngine\localiseLineNumbersCb
localiseLineNumbersCb( $matches)
Definition: DifferenceEngine.php:968
MWException
MediaWiki exception.
Definition: MWException.php:26
wfMemcKey
wfMemcKey()
Get a cache key.
Definition: GlobalFunctions.php:3635
$out
$out
Definition: UtfNormalGenerate.php:167
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:103
DifferenceEngine\wasCacheHit
wasCacheHit()
Definition: DifferenceEngine.php:129
Html\element
static element( $element, $attribs=array(), $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:148
wfIncrStats
wfIncrStats( $key, $count=1)
Increment a statistics counter.
Definition: GlobalFunctions.php:1351
DifferenceEngine\getDiffLang
getDiffLang()
Definition: DifferenceEngine.php:117
Linker\revUserTools
static revUserTools( $rev, $isPublic=false)
Generate a user tool link cluster if the current user is allowed to view it.
Definition: Linker.php:1228
ContextSource\getOutput
getOutput()
Get the OutputPage object.
Definition: ContextSource.php:122
ContextSource
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
Definition: ContextSource.php:30
ContextSource\getWikiPage
getWikiPage()
Get the WikiPage object.
Definition: ContextSource.php:112
DifferenceEngine\$mOldContent
Content $mOldContent
Definition: DifferenceEngine.php:45
wfProfileOut
wfProfileOut( $functionname='missing')
Stop profiling of a function.
Definition: Profiler.php:46
wfMessage
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 just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing after in associative array form externallinks including delete and has completed for all link tables default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4066
DifferenceEngine\deletedLink
deletedLink( $id)
Look up a special:Undelete link to the given deleted revision id, as a workaround for being unable to...
Definition: DifferenceEngine.php:159
wfGetLangObj
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
Definition: GlobalFunctions.php:1399
DifferenceEngine\getDiffBody
getDiffBody()
Get the diff table body, without header.
Definition: DifferenceEngine.php:670
DifferenceEngine\getRevisionHeader
getRevisionHeader(Revision $rev, $complete='')
Get a header for a specified revision.
Definition: DifferenceEngine.php:1046
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
DifferenceEngine\loadText
loadText()
Load the text of the revisions, as well as revision data.
Definition: DifferenceEngine.php:1331
ContextSource\setContext
setContext(IContextSource $context)
Set the IContextSource object.
Definition: ContextSource.php:57
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
DifferenceEngine\$mCacheHit
bool $mCacheHit
Was the diff fetched from cache? *.
Definition: DifferenceEngine.php:65
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:144
MW_DIFF_VERSION
const MW_DIFF_VERSION
Constant to indicate diff cache compatibility.
Definition: DifferenceEngine.php:30
DifferenceEngine\$enableDebugComment
$enableDebugComment
Set this to true to add debug info to the HTML output.
Definition: DifferenceEngine.php:72
DifferenceEngine\__construct
__construct( $context=null, $old=0, $new=0, $rcid=0, $refreshCache=false, $unhide=false)
#-
Definition: DifferenceEngine.php:92
Revision\newFromTitle
static newFromTitle( $title, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given title.
Definition: Revision.php:106
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:980
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:422
Linker\revComment
static revComment(Revision $rev, $local=false, $isPublic=false)
Wrap and format the given revision's comment block, if the current user is allowed to view it.
Definition: Linker.php:1587
$title
presenting them properly to the user as errors is done by the caller $title
Definition: hooks.txt:1324
see
Some information about database access in MediaWiki By Tim January Database layout For information about the MediaWiki database such as a description of the tables and their please see
Definition: database.txt:2
$matches
if(!defined( 'MEDIAWIKI')) if(!isset( $wgVersion)) $matches
Definition: NoLocalSettings.php:33
$value
$value
Definition: styleTest.css.php:45
Linker\getRevDeleteLink
static getRevDeleteLink(User $user, Revision $rev, Title $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2164
wfEscapeShellArg
wfEscapeShellArg()
Windows-compatible version of escapeshellarg() Windows doesn't recognise single-quotes in the shell,...
Definition: GlobalFunctions.php:2752
DifferenceEngine\setText
setText( $oldText, $newText)
Use specified text instead of loading from the database.
Definition: DifferenceEngine.php:1155
Revision\newFromArchiveRow
static newFromArchiveRow( $row, $overrides=array())
Make a fake revision object from an archive table row.
Definition: Revision.php:159
DifferenceEngine\showDiffPage
showDiffPage( $diffOnly=false)
Definition: DifferenceEngine.php:215
$suppressed
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging a wrapping ErrorException $suppressed
Definition: hooks.txt:1632
$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:237
DifferenceEngine\generateDiffBody
generateDiffBody( $otext, $ntext)
Generate a diff, no caching.
Definition: DifferenceEngine.php:809
TextContent
Content object implementation for representing flat text.
Definition: TextContent.php:35
IContextSource
Interface for objects which can provide a context on request.
Definition: IContextSource.php:29
DifferenceEngine\intermediateEditsMsg
static intermediateEditsMsg( $numEdits, $numUsers, $limit)
Get a notice about how many intermediate edits and users there are.
Definition: DifferenceEngine.php:1024
DifferenceEngine\showMissingRevision
showMissingRevision()
Definition: DifferenceEngine.php:195
Content
Base interface for content objects.
Definition: Content.php:34
DifferenceEngine\$mOldRev
Revision $mOldRev
Definition: DifferenceEngine.php:55
DifferenceEngine\loadRevisionIds
loadRevisionIds()
Load revision IDs.
Definition: DifferenceEngine.php:1216
DifferenceEngine\getParserOutput
getParserOutput(WikiPage $page, Revision $rev)
Definition: DifferenceEngine.php:596
$self
$self
Definition: doMaintenance.php:54
$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:1337
DB_SLAVE
const DB_SLAVE
Definition: Defines.php:55
Title
Represents a title within MediaWiki.
Definition: Title.php:35
wfTempDir
wfTempDir()
Tries to get the system directory for temporary files.
Definition: GlobalFunctions.php:2611
DifferenceEngine\$mDiffLang
Language $mDiffLang
Definition: DifferenceEngine.php:49
FatalError
Exception class which takes an HTML error message, and does not produce a backtrace.
Definition: FatalError.php:28
Linker\titleAttrib
static titleAttrib( $name, $options=null)
Given the id of an interface element, constructs the appropriate title attribute from the system mess...
Definition: Linker.php:2081
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
Definition: DifferenceEngine.php:36
RecentChange\isInRCLifespan
static isInRCLifespan( $timestamp, $tolerance=0)
Check whether the given timestamp is new enough to have a RC row with a given tolerance as the recent...
Definition: RecentChange.php:877
DifferenceEngine\$mMarkPatrolledLink
string $mMarkPatrolledLink
Link to action=markpatrolled *.
Definition: DifferenceEngine.php:78
DifferenceEngine\localiseLineNumbers
localiseLineNumbers( $text)
Replace line numbers with the text in the user's language.
Definition: DifferenceEngine.php:960
DifferenceEngine\debug
debug( $generator="internal")
Generate a debug comment indicating diff generating time, server node, and generator backend.
Definition: DifferenceEngine.php:937
DifferenceEngine\renderNewRevision
renderNewRevision()
Show the new revision of the page.
Definition: DifferenceEngine.php:524
$error
usually copyright or history_copyright This message must be in HTML not wikitext $subpages will be ignored and the rest of subPageSubtitle() will run. 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink' whether MediaWiki currently thinks this is a CSS JS page Hooks may change this value to override the return value of Title::isCssOrJsPage(). 'TitleIsAlwaysKnown' whether MediaWiki currently thinks this page is known isMovable() always returns false. $title whether MediaWiki currently thinks this page is movable Hooks may change this value to override the return value of Title::isMovable(). 'TitleIsWikitextPage' whether MediaWiki currently thinks this is a wikitext page Hooks may change this value to override the return value of Title::isWikitextPage() 'TitleMove' use UploadVerification and UploadVerifyFile instead where the first element is the message key and the remaining elements are used as parameters to the message based on mime etc Preferred in most cases over UploadVerification object with all info about the upload string as detected by MediaWiki Handlers will typically only apply for specific mime types object & $error
Definition: hooks.txt:2584
Html\rawElement
static rawElement( $element, $attribs=array(), $contents='')
Returns an HTML element in a string.
Definition: Html.php:124
DifferenceEngine\getDiff
getDiff( $otitle, $ntitle, $notice='')
Get complete diff table, including header.
Definition: DifferenceEngine.php:648
$query
return true to allow those checks to and false if checking is done use this to change the tables headers temp or archived zone change it to an object instance and return false override the list derivative used the name of the old file when set the default code will be skipped add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition: hooks.txt:1105
DifferenceEngine\generateContentDiffBody
generateContentDiffBody(Content $old, Content $new)
Generate a diff, no caching.
Definition: DifferenceEngine.php:783
DifferenceEngine\mapDiffPrevNext
mapDiffPrevNext( $old, $new)
Maps a revision pair definition as accepted by DifferenceEngine constructor to a pair of actual integ...
Definition: DifferenceEngine.php:1196
$article
Using a hook running we can avoid having all this option specific stuff in our mainline code Using the function array $article
Definition: hooks.txt:78
DifferenceEngine\addHeader
addHeader( $diff, $otitle, $ntitle, $multi='', $notice='')
Add the header to a diff body.
Definition: DifferenceEngine.php:1106
Revision\DELETED_TEXT
const DELETED_TEXT
Definition: Revision.php:65
Language
Internationalisation code.
Definition: Language.php:74
DifferenceEngine\textDiff
textDiff( $otext, $ntext)
Generates diff, to be wrapped internally in a logging/instrumentation.
Definition: DifferenceEngine.php:854
WikiPage\getParserOutput
getParserOutput(ParserOptions $parserOptions, $oldid=null)
Get a ParserOutput for the given ParserOptions and revision ID.
Definition: WikiPage.php:1138
ChangeTags\formatSummaryRow
static formatSummaryRow( $tags, $page)
Creates HTML for the given tags.
Definition: ChangeTags.php:34
Article\newFromTitle
static newFromTitle( $title, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:142
Diff
Class representing a 'diff' between two sequences of strings.
Definition: DairikiDiff.php:705
DifferenceEngine\getMultiNotice
getMultiNotice()
If there are revisions between the ones being compared, return a note saying so.
Definition: DifferenceEngine.php:981