MediaWiki  1.23.12
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(),
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  global $wgExternalDiffEngine, $wgContLang;
843 
844  wfProfileIn( __METHOD__ );
845 
846  $otext = str_replace( "\r\n", "\n", $otext );
847  $ntext = str_replace( "\r\n", "\n", $ntext );
848 
849  if ( $wgExternalDiffEngine == 'wikidiff' && function_exists( 'wikidiff_do_diff' ) ) {
850  # For historical reasons, external diff engine expects
851  # input text to be HTML-escaped already
852  $otext = htmlspecialchars( $wgContLang->segmentForDiff( $otext ) );
853  $ntext = htmlspecialchars( $wgContLang->segmentForDiff( $ntext ) );
854  wfProfileOut( __METHOD__ );
855 
856  return $wgContLang->unsegmentForDiff( wikidiff_do_diff( $otext, $ntext, 2 ) ) .
857  $this->debug( 'wikidiff1' );
858  }
859 
860  if ( $wgExternalDiffEngine == 'wikidiff2' && function_exists( 'wikidiff2_do_diff' ) ) {
861  # Better external diff engine, the 2 may some day be dropped
862  # This one does the escaping and segmenting itself
863  wfProfileIn( 'wikidiff2_do_diff' );
864  $text = wikidiff2_do_diff( $otext, $ntext, 2 );
865  $text .= $this->debug( 'wikidiff2' );
866  wfProfileOut( 'wikidiff2_do_diff' );
867  wfProfileOut( __METHOD__ );
868 
869  return $text;
870  }
871  if ( $wgExternalDiffEngine != 'wikidiff3' && $wgExternalDiffEngine !== false ) {
872  # Diff via the shell
873  $tmpDir = wfTempDir();
874  $tempName1 = tempnam( $tmpDir, 'diff_' );
875  $tempName2 = tempnam( $tmpDir, 'diff_' );
876 
877  $tempFile1 = fopen( $tempName1, "w" );
878  if ( !$tempFile1 ) {
879  wfProfileOut( __METHOD__ );
880 
881  return false;
882  }
883  $tempFile2 = fopen( $tempName2, "w" );
884  if ( !$tempFile2 ) {
885  wfProfileOut( __METHOD__ );
886 
887  return false;
888  }
889  fwrite( $tempFile1, $otext );
890  fwrite( $tempFile2, $ntext );
891  fclose( $tempFile1 );
892  fclose( $tempFile2 );
893  $cmd = wfEscapeShellArg( $wgExternalDiffEngine, $tempName1, $tempName2 );
894  wfProfileIn( __METHOD__ . "-shellexec" );
895  $difftext = wfShellExec( $cmd );
896  $difftext .= $this->debug( "external $wgExternalDiffEngine" );
897  wfProfileOut( __METHOD__ . "-shellexec" );
898  unlink( $tempName1 );
899  unlink( $tempName2 );
900  wfProfileOut( __METHOD__ );
901 
902  return $difftext;
903  }
904 
905  # Native PHP diff
906  $ota = explode( "\n", $wgContLang->segmentForDiff( $otext ) );
907  $nta = explode( "\n", $wgContLang->segmentForDiff( $ntext ) );
908  $diffs = new Diff( $ota, $nta );
909  $formatter = new TableDiffFormatter();
910  $difftext = $wgContLang->unsegmentForDiff( $formatter->format( $diffs ) ) .
911  wfProfileOut( __METHOD__ );
912 
913  return $difftext;
914  }
915 
924  protected function debug( $generator = "internal" ) {
925  global $wgShowHostnames;
926  if ( !$this->enableDebugComment ) {
927  return '';
928  }
929  $data = array( $generator );
930  if ( $wgShowHostnames ) {
931  $data[] = wfHostname();
932  }
933  $data[] = wfTimestamp( TS_DB );
934 
935  return "<!-- diff generator: " .
936  implode( " ", array_map( "htmlspecialchars", $data ) ) .
937  " -->\n";
938  }
939 
947  public function localiseLineNumbers( $text ) {
948  return preg_replace_callback(
949  '/<!--LINE (\d+)-->/',
950  array( &$this, 'localiseLineNumbersCb' ),
951  $text
952  );
953  }
954 
955  public function localiseLineNumbersCb( $matches ) {
956  if ( $matches[1] === '1' && $this->mReducedLineNumbers ) {
957  return '';
958  }
959 
960  return $this->msg( 'lineno' )->numParams( $matches[1] )->escaped();
961  }
962 
968  public function getMultiNotice() {
969  if ( !is_object( $this->mOldRev ) || !is_object( $this->mNewRev ) ) {
970  return '';
971  } elseif ( !$this->mOldPage->equals( $this->mNewPage ) ) {
972  // Comparing two different pages? Count would be meaningless.
973  return '';
974  }
975 
976  if ( $this->mOldRev->getTimestamp() > $this->mNewRev->getTimestamp() ) {
977  $oldRev = $this->mNewRev; // flip
978  $newRev = $this->mOldRev; // flip
979  } else { // normal case
980  $oldRev = $this->mOldRev;
981  $newRev = $this->mNewRev;
982  }
983 
984  // Sanity: don't show the notice if too many rows must be scanned
985  // @TODO: show some special message for that case
986  $nEdits = $this->mNewPage->countRevisionsBetween( $oldRev, $newRev, 1000 );
987  if ( $nEdits > 0 && $nEdits <= 1000 ) {
988  $limit = 100; // use diff-multi-manyusers if too many users
989  $users = $this->mNewPage->getAuthorsBetween( $oldRev, $newRev, $limit );
990  $numUsers = count( $users );
991 
992  if ( $numUsers == 1 && $users[0] == $newRev->getRawUserText() ) {
993  $numUsers = 0; // special case to say "by the same user" instead of "by one other user"
994  }
995 
996  return self::intermediateEditsMsg( $nEdits, $numUsers, $limit );
997  }
998 
999  return ''; // nothing
1000  }
1001 
1011  public static function intermediateEditsMsg( $numEdits, $numUsers, $limit ) {
1012  if ( $numUsers === 0 ) {
1013  $msg = 'diff-multi-sameuser';
1014  } elseif ( $numUsers > $limit ) {
1015  $msg = 'diff-multi-manyusers';
1016  $numUsers = $limit;
1017  } else {
1018  $msg = 'diff-multi-otherusers';
1019  }
1020 
1021  return wfMessage( $msg )->numParams( $numEdits, $numUsers )->parse();
1022  }
1023 
1033  protected function getRevisionHeader( Revision $rev, $complete = '' ) {
1034  $lang = $this->getLanguage();
1035  $user = $this->getUser();
1036  $revtimestamp = $rev->getTimestamp();
1037  $timestamp = $lang->userTimeAndDate( $revtimestamp, $user );
1038  $dateofrev = $lang->userDate( $revtimestamp, $user );
1039  $timeofrev = $lang->userTime( $revtimestamp, $user );
1040 
1041  $header = $this->msg(
1042  $rev->isCurrent() ? 'currentrev-asof' : 'revisionasof',
1043  $timestamp,
1044  $dateofrev,
1045  $timeofrev
1046  )->escaped();
1047 
1048  if ( $complete !== 'complete' ) {
1049  return $header;
1050  }
1051 
1052  $title = $rev->getTitle();
1053 
1054  $header = Linker::linkKnown( $title, $header, array(),
1055  array( 'oldid' => $rev->getID() ) );
1056 
1057  if ( $rev->userCan( Revision::DELETED_TEXT, $user ) ) {
1058  $editQuery = array( 'action' => 'edit' );
1059  if ( !$rev->isCurrent() ) {
1060  $editQuery['oldid'] = $rev->getID();
1061  }
1062 
1063  $key = $title->quickUserCan( 'edit', $user ) ? 'editold' : 'viewsourceold';
1064  $msg = $this->msg( $key )->escaped();
1065  $header .= ' ' . $this->msg( 'parentheses' )->rawParams(
1066  Linker::linkKnown( $title, $msg, array(), $editQuery ) )->plain();
1067  if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
1068  $header = Html::rawElement(
1069  'span',
1070  array( 'class' => 'history-deleted' ),
1071  $header
1072  );
1073  }
1074  } else {
1075  $header = Html::rawElement( 'span', array( 'class' => 'history-deleted' ), $header );
1076  }
1078  return $header;
1079  }
1080 
1093  public function addHeader( $diff, $otitle, $ntitle, $multi = '', $notice = '' ) {
1094  // shared.css sets diff in interface language/dir, but the actual content
1095  // is often in a different language, mostly the page content language/dir
1096  $tableClass = 'diff diff-contentalign-' . htmlspecialchars( $this->getDiffLang()->alignStart() );
1097  $header = "<table class='$tableClass'>";
1098 
1099  if ( !$diff && !$otitle ) {
1100  $header .= "
1101  <tr style='vertical-align: top;'>
1102  <td class='diff-ntitle'>{$ntitle}</td>
1103  </tr>";
1104  $multiColspan = 1;
1105  } else {
1106  if ( $diff ) { // Safari/Chrome show broken output if cols not used
1107  $header .= "
1108  <col class='diff-marker' />
1109  <col class='diff-content' />
1110  <col class='diff-marker' />
1111  <col class='diff-content' />";
1112  $colspan = 2;
1113  $multiColspan = 4;
1114  } else {
1115  $colspan = 1;
1116  $multiColspan = 2;
1117  }
1118  if ( $otitle || $ntitle ) {
1119  $header .= "
1120  <tr style='vertical-align: top;'>
1121  <td colspan='$colspan' class='diff-otitle'>{$otitle}</td>
1122  <td colspan='$colspan' class='diff-ntitle'>{$ntitle}</td>
1123  </tr>";
1124  }
1125  }
1127  if ( $multi != '' ) {
1128  $header .= "<tr><td colspan='{$multiColspan}' style='text-align: center;' " .
1129  "class='diff-multi'>{$multi}</td></tr>";
1130  }
1131  if ( $notice != '' ) {
1132  $header .= "<tr><td colspan='{$multiColspan}' style='text-align: center;'>{$notice}</td></tr>";
1133  }
1134 
1135  return $header . $diff . "</table>";
1136  }
1137 
1142  public function setText( $oldText, $newText ) {
1143  ContentHandler::deprecated( __METHOD__, "1.21" );
1144 
1145  $oldContent = ContentHandler::makeContent( $oldText, $this->getTitle() );
1146  $newContent = ContentHandler::makeContent( $newText, $this->getTitle() );
1147 
1148  $this->setContent( $oldContent, $newContent );
1149  }
1150 
1155  public function setContent( Content $oldContent, Content $newContent ) {
1156  $this->mOldContent = $oldContent;
1157  $this->mNewContent = $newContent;
1158 
1159  $this->mTextLoaded = 2;
1160  $this->mRevisionsLoaded = true;
1161  }
1162 
1168  public function setTextLanguage( $lang ) {
1169  $this->mDiffLang = wfGetLangObj( $lang );
1170  }
1171 
1183  public function mapDiffPrevNext( $old, $new ) {
1184  if ( $new === 'prev' ) {
1185  // Show diff between revision $old and the previous one. Get previous one from DB.
1186  $newid = intval( $old );
1187  $oldid = $this->getTitle()->getPreviousRevisionID( $newid );
1188  } elseif ( $new === 'next' ) {
1189  // Show diff between revision $old and the next one. Get next one from DB.
1190  $oldid = intval( $old );
1191  $newid = $this->getTitle()->getNextRevisionID( $oldid );
1192  } else {
1193  $oldid = intval( $old );
1194  $newid = intval( $new );
1195  }
1196 
1197  return array( $oldid, $newid );
1198  }
1199 
1203  private function loadRevisionIds() {
1204  if ( $this->mRevisionsIdsLoaded ) {
1205  return;
1206  }
1207 
1208  $this->mRevisionsIdsLoaded = true;
1209 
1210  $old = $this->mOldid;
1211  $new = $this->mNewid;
1212 
1213  list( $this->mOldid, $this->mNewid ) = self::mapDiffPrevNext( $old, $new );
1214  if ( $new === 'next' && $this->mNewid === false ) {
1215  # if no result, NewId points to the newest old revision. The only newer
1216  # revision is cur, which is "0".
1217  $this->mNewid = 0;
1218  }
1219 
1220  wfRunHooks(
1221  'NewDifferenceEngine',
1222  array( $this->getTitle(), &$this->mOldid, &$this->mNewid, $old, $new )
1223  );
1224  }
1225 
1238  public function loadRevisionData() {
1239  if ( $this->mRevisionsLoaded ) {
1240  return true;
1241  }
1242 
1243  // Whether it succeeds or fails, we don't want to try again
1244  $this->mRevisionsLoaded = true;
1245 
1246  $this->loadRevisionIds();
1247 
1248  // Load the new revision object
1249  if ( $this->mNewid ) {
1250  $this->mNewRev = Revision::newFromId( $this->mNewid );
1251  } else {
1252  $this->mNewRev = Revision::newFromTitle(
1253  $this->getTitle(),
1254  false,
1256  );
1257  }
1258 
1259  if ( !$this->mNewRev instanceof Revision ) {
1260  return false;
1261  }
1262 
1263  // Update the new revision ID in case it was 0 (makes life easier doing UI stuff)
1264  $this->mNewid = $this->mNewRev->getId();
1265  $this->mNewPage = $this->mNewRev->getTitle();
1266 
1267  // Load the old revision object
1268  $this->mOldRev = false;
1269  if ( $this->mOldid ) {
1270  $this->mOldRev = Revision::newFromId( $this->mOldid );
1271  } elseif ( $this->mOldid === 0 ) {
1272  $rev = $this->mNewRev->getPrevious();
1273  if ( $rev ) {
1274  $this->mOldid = $rev->getId();
1275  $this->mOldRev = $rev;
1276  } else {
1277  // No previous revision; mark to show as first-version only.
1278  $this->mOldid = false;
1279  $this->mOldRev = false;
1280  }
1281  } /* elseif ( $this->mOldid === false ) leave mOldRev false; */
1282 
1283  if ( is_null( $this->mOldRev ) ) {
1284  return false;
1285  }
1286 
1287  if ( $this->mOldRev ) {
1288  $this->mOldPage = $this->mOldRev->getTitle();
1289  }
1290 
1291  // Load tags information for both revisions
1292  $dbr = wfGetDB( DB_SLAVE );
1293  if ( $this->mOldid !== false ) {
1294  $this->mOldTags = $dbr->selectField(
1295  'tag_summary',
1296  'ts_tags',
1297  array( 'ts_rev_id' => $this->mOldid ),
1298  __METHOD__
1299  );
1300  } else {
1301  $this->mOldTags = false;
1302  }
1303  $this->mNewTags = $dbr->selectField(
1304  'tag_summary',
1305  'ts_tags',
1306  array( 'ts_rev_id' => $this->mNewid ),
1307  __METHOD__
1308  );
1309 
1310  return true;
1311  }
1312 
1318  public function loadText() {
1319  if ( $this->mTextLoaded == 2 ) {
1320  return true;
1321  }
1322 
1323  // Whether it succeeds or fails, we don't want to try again
1324  $this->mTextLoaded = 2;
1325 
1326  if ( !$this->loadRevisionData() ) {
1327  return false;
1328  }
1329 
1330  if ( $this->mOldRev ) {
1331  $this->mOldContent = $this->mOldRev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
1332  if ( $this->mOldContent === null ) {
1333  return false;
1334  }
1335  }
1337  if ( $this->mNewRev ) {
1338  $this->mNewContent = $this->mNewRev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
1339  if ( $this->mNewContent === null ) {
1340  return false;
1341  }
1342  }
1343 
1344  return true;
1345  }
1346 
1352  public function loadNewText() {
1353  if ( $this->mTextLoaded >= 1 ) {
1354  return true;
1355  }
1356 
1357  $this->mTextLoaded = 1;
1358 
1359  if ( !$this->loadRevisionData() ) {
1360  return false;
1361  }
1362 
1363  $this->mNewContent = $this->mNewRev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
1364 
1365  return true;
1366  }
1367 
1368 }
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:1770
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:1152
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:3706
$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:1139
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
$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:1336
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:2154
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:1222
DifferenceEngine\localiseLineNumbersCb
localiseLineNumbersCb( $matches)
Definition: DifferenceEngine.php:939
MWException
MediaWiki exception.
Definition: MWException.php:26
wfMemcKey
wfMemcKey()
Get a cache key.
Definition: GlobalFunctions.php:3627
$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:1219
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:4058
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:1017
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:1302
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:1578
$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:2155
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:1126
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:995
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:1187
DifferenceEngine\getParserOutput
getParserOutput(WikiPage $page, Revision $rev)
Definition: DifferenceEngine.php:596
$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
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:2072
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:931
DifferenceEngine\debug
debug( $generator="internal")
Generate a debug comment indicating diff generating time, server node, and generator backend.
Definition: DifferenceEngine.php:908
DifferenceEngine\renderNewRevision
renderNewRevision()
Show the new revision of the page.
Definition: DifferenceEngine.php:524
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:1167
$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:1077
Revision\DELETED_TEXT
const DELETED_TEXT
Definition: Revision.php:65
Language
Internationalisation code.
Definition: Language.php:74
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:952