r57243 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r57242‎ | r57243 | r57244 >
Date:19:32, 1 October 2009
Author:brion
Status:ok
Tags:
Comment:
fix eol style
Modified paths:
  • /trunk/phase3/includes/diff/DifferenceInterface.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/diff/DifferenceInterface.php
@@ -1,1127 +1,1127 @@
2 -<?php
3 -/**
4 - * @defgroup DifferenceEngine DifferenceEngine
5 - */
6 -
7 -/**
8 - * Constant to indicate diff cache compatibility.
9 - * Bump this when changing the diff formatting in a way that
10 - * fixes important bugs or such to force cached diff views to
11 - * clear.
12 - */
13 -define( 'MW_DIFF_VERSION', '1.11a' );
14 -
15 -/**
16 - * @todo document
17 - * @ingroup DifferenceEngine
18 - */
19 -class DifferenceEngine {
20 - /**#@+
21 - * @private
22 - */
23 - var $mOldid, $mNewid, $mTitle;
24 - var $mOldtitle, $mNewtitle, $mPagetitle;
25 - var $mOldtext, $mNewtext;
26 - var $mOldPage, $mNewPage;
27 - var $mRcidMarkPatrolled;
28 - var $mOldRev, $mNewRev;
29 - var $mRevisionsLoaded = false; // Have the revisions been loaded
30 - var $mTextLoaded = 0; // How many text blobs have been loaded, 0, 1 or 2?
31 - var $mCacheHit = false; // Was the diff fetched from cache?
32 - var $htmldiff;
33 -
34 - /**
35 - * Set this to true to add debug info to the HTML output.
36 - * Warning: this may cause RSS readers to spuriously mark articles as "new"
37 - * (bug 20601)
38 - */
39 - var $enableDebugComment = false;
40 -
41 - // If true, line X is not displayed when X is 1, for example to increase
42 - // readability and conserve space with many small diffs.
43 - protected $mReducedLineNumbers = false;
44 -
45 - protected $unhide = false;
46 - /**#@-*/
47 -
48 - /**
49 - * Constructor
50 - * @param $titleObj Title object that the diff is associated with
51 - * @param $old Integer: old ID we want to show and diff with.
52 - * @param $new String: either 'prev' or 'next'.
53 - * @param $rcid Integer: ??? FIXME (default 0)
54 - * @param $refreshCache boolean If set, refreshes the diff cache
55 - * @param $htmldiff boolean If set, output using HTMLDiff instead of raw wikicode diff
56 - * @param $unhide boolean If set, allow viewing deleted revs
57 - */
58 - function __construct( $titleObj = null, $old = 0, $new = 0, $rcid = 0,
59 - $refreshCache = false, $htmldiff = false, $unhide = false )
60 - {
61 - if ( $titleObj ) {
62 - $this->mTitle = $titleObj;
63 - } else {
64 - global $wgTitle;
65 - $this->mTitle = $wgTitle;
66 - }
67 - wfDebug("DifferenceEngine old '$old' new '$new' rcid '$rcid'\n");
68 -
69 - if ( 'prev' === $new ) {
70 - # Show diff between revision $old and the previous one.
71 - # Get previous one from DB.
72 - $this->mNewid = intval($old);
73 - $this->mOldid = $this->mTitle->getPreviousRevisionID( $this->mNewid );
74 - } elseif ( 'next' === $new ) {
75 - # Show diff between revision $old and the next one.
76 - # Get next one from DB.
77 - $this->mOldid = intval($old);
78 - $this->mNewid = $this->mTitle->getNextRevisionID( $this->mOldid );
79 - if ( false === $this->mNewid ) {
80 - # if no result, NewId points to the newest old revision. The only newer
81 - # revision is cur, which is "0".
82 - $this->mNewid = 0;
83 - }
84 - } else {
85 - $this->mOldid = intval($old);
86 - $this->mNewid = intval($new);
87 - wfRunHooks( 'NewDifferenceEngine', array(&$titleObj, &$this->mOldid, &$this->mNewid, $old, $new) );
88 - }
89 - $this->mRcidMarkPatrolled = intval($rcid); # force it to be an integer
90 - $this->mRefreshCache = $refreshCache;
91 - $this->htmldiff = $htmldiff;
92 - $this->unhide = $unhide;
93 - }
94 -
95 - function setReducedLineNumbers( $value = true ) {
96 - $this->mReducedLineNumbers = $value;
97 - }
98 -
99 - function getTitle() {
100 - return $this->mTitle;
101 - }
102 -
103 - function wasCacheHit() {
104 - return $this->mCacheHit;
105 - }
106 -
107 - function getOldid() {
108 - return $this->mOldid;
109 - }
110 -
111 - function getNewid() {
112 - return $this->mNewid;
113 - }
114 -
115 - function showDiffPage( $diffOnly = false ) {
116 - global $wgUser, $wgOut, $wgUseExternalEditor, $wgUseRCPatrol, $wgEnableHtmlDiff;
117 - wfProfileIn( __METHOD__ );
118 -
119 -
120 - # If external diffs are enabled both globally and for the user,
121 - # we'll use the application/x-external-editor interface to call
122 - # an external diff tool like kompare, kdiff3, etc.
123 - if($wgUseExternalEditor && $wgUser->getOption('externaldiff')) {
124 - global $wgInputEncoding,$wgServer,$wgScript,$wgLang;
125 - $wgOut->disable();
126 - header ( "Content-type: application/x-external-editor; charset=".$wgInputEncoding );
127 - $url1=$this->mTitle->getFullURL( array(
128 - 'action' => 'raw',
129 - 'oldid' => $this->mOldid
130 - ) );
131 - $url2=$this->mTitle->getFullURL( array(
132 - 'action' => 'raw',
133 - 'oldid' => $this->mNewid
134 - ) );
135 - $special=$wgLang->getNsText(NS_SPECIAL);
136 - $control=<<<CONTROL
137 - [Process]
138 - Type=Diff text
139 - Engine=MediaWiki
140 - Script={$wgServer}{$wgScript}
141 - Special namespace={$special}
142 -
143 - [File]
144 - Extension=wiki
145 - URL=$url1
146 -
147 - [File 2]
148 - Extension=wiki
149 - URL=$url2
150 -CONTROL;
151 - echo($control);
152 - return;
153 - }
154 -
155 - $wgOut->setArticleFlag( false );
156 - if ( !$this->loadRevisionData() ) {
157 - $t = $this->mTitle->getPrefixedText();
158 - $d = wfMsgExt( 'missingarticle-diff', array( 'escape' ), $this->mOldid, $this->mNewid );
159 - $wgOut->setPagetitle( wfMsg( 'errorpagetitle' ) );
160 - $wgOut->addWikiMsg( 'missing-article', "<nowiki>$t</nowiki>", $d );
161 - wfProfileOut( __METHOD__ );
162 - return;
163 - }
164 -
165 - wfRunHooks( 'DiffViewHeader', array( $this, $this->mOldRev, $this->mNewRev ) );
166 -
167 - if ( $this->mNewRev->isCurrent() ) {
168 - $wgOut->setArticleFlag( true );
169 - }
170 -
171 - # mOldid is false if the difference engine is called with a "vague" query for
172 - # a diff between a version V and its previous version V' AND the version V
173 - # is the first version of that article. In that case, V' does not exist.
174 - if ( $this->mOldid === false ) {
175 - $this->showFirstRevision();
176 - $this->renderNewRevision(); // should we respect $diffOnly here or not?
177 - wfProfileOut( __METHOD__ );
178 - return;
179 - }
180 -
181 - $wgOut->suppressQuickbar();
182 -
183 - $oldTitle = $this->mOldPage->getPrefixedText();
184 - $newTitle = $this->mNewPage->getPrefixedText();
185 - if( $oldTitle == $newTitle ) {
186 - $wgOut->setPageTitle( $newTitle );
187 - } else {
188 - $wgOut->setPageTitle( $oldTitle . ', ' . $newTitle );
189 - }
190 - $wgOut->setSubtitle( wfMsgExt( 'difference', array( 'parseinline' ) ) );
191 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
192 -
193 - if ( !$this->mOldPage->userCanRead() || !$this->mNewPage->userCanRead() ) {
194 - $wgOut->loginToUse();
195 - $wgOut->output();
196 - $wgOut->disable();
197 - wfProfileOut( __METHOD__ );
198 - return;
199 - }
200 -
201 - $sk = $wgUser->getSkin();
202 -
203 - // Check if page is editable
204 - $editable = $this->mNewRev->getTitle()->userCan( 'edit' );
205 - if ( $editable && $this->mNewRev->isCurrent() && $wgUser->isAllowed( 'rollback' ) ) {
206 - $rollback = '&nbsp;&nbsp;&nbsp;' . $sk->generateRollback( $this->mNewRev );
207 - } else {
208 - $rollback = '';
209 - }
210 -
211 - // Prepare a change patrol link, if applicable
212 - if( $wgUseRCPatrol && $this->mTitle->userCan('patrol') ) {
213 - // If we've been given an explicit change identifier, use it; saves time
214 - if( $this->mRcidMarkPatrolled ) {
215 - $rcid = $this->mRcidMarkPatrolled;
216 - $rc = RecentChange::newFromId( $rcid );
217 - // Already patrolled?
218 - $rcid = is_object($rc) && !$rc->getAttribute('rc_patrolled') ? $rcid : 0;
219 - } else {
220 - // Look for an unpatrolled change corresponding to this diff
221 - $db = wfGetDB( DB_SLAVE );
222 - $change = RecentChange::newFromConds(
223 - array(
224 - // Redundant user,timestamp condition so we can use the existing index
225 - 'rc_user_text' => $this->mNewRev->getRawUserText(),
226 - 'rc_timestamp' => $db->timestamp( $this->mNewRev->getTimestamp() ),
227 - 'rc_this_oldid' => $this->mNewid,
228 - 'rc_last_oldid' => $this->mOldid,
229 - 'rc_patrolled' => 0
230 - ),
231 - __METHOD__
232 - );
233 - if( $change instanceof RecentChange ) {
234 - $rcid = $change->mAttribs['rc_id'];
235 - $this->mRcidMarkPatrolled = $rcid;
236 - } else {
237 - // None found
238 - $rcid = 0;
239 - }
240 - }
241 - // Build the link
242 - if( $rcid ) {
243 - $patrol = ' <span class="patrollink">[' . $sk->link(
244 - $this->mTitle,
245 - wfMsgHtml( 'markaspatrolleddiff' ),
246 - array(),
247 - array(
248 - 'action' => 'markpatrolled',
249 - 'rcid' => $rcid
250 - ),
251 - array(
252 - 'known',
253 - 'noclasses'
254 - )
255 - ) . ']</span>';
256 - } else {
257 - $patrol = '';
258 - }
259 - } else {
260 - $patrol = '';
261 - }
262 -
263 - # Carry over 'diffonly' param via navigation links
264 - if( $diffOnly != $wgUser->getBoolOption('diffonly') ) {
265 - $query['diffonly'] = $diffOnly;
266 - }
267 -
268 - $htmldiffarg = $this->htmlDiffArgument();
269 -
270 - if( $htmldiffarg ) {
271 - $query['htmldiff'] = $htmldiffarg['htmldiff'];
272 - }
273 -
274 - # Make "previous revision link"
275 - $query['diff'] = 'prev';
276 - $query['oldid'] = $this->mOldid;
277 -
278 - $prevlink = $sk->link(
279 - $this->mTitle,
280 - wfMsgHtml( 'previousdiff' ),
281 - array(
282 - 'id' => 'differences-prevlink'
283 - ),
284 - $query,
285 - array(
286 - 'known',
287 - 'noclasses'
288 - )
289 - );
290 - # Make "next revision link"
291 - $query['diff'] = 'next';
292 - $query['oldid'] = $this->mNewid;
293 -
294 - if( $this->mNewRev->isCurrent() ) {
295 - $nextlink = '&nbsp;';
296 - } else {
297 - $nextlink = $sk->link(
298 - $this->mTitle,
299 - wfMsgHtml( 'nextdiff' ),
300 - array(
301 - 'id' => 'differences-nextlink'
302 - ),
303 - $query,
304 - array(
305 - 'known',
306 - 'noclasses'
307 - )
308 - );
309 - }
310 -
311 - $oldminor = '';
312 - $newminor = '';
313 -
314 - if( $this->mOldRev->isMinor() ) {
315 - $oldminor = ChangesList::flag( 'minor' );
316 - }
317 - if( $this->mNewRev->isMinor() ) {
318 - $newminor = ChangesList::flag( 'minor' );
319 - }
320 -
321 - $rdel = ''; $ldel = '';
322 - if( $wgUser->isAllowed( 'deletedhistory' ) ) {
323 - // Don't show useless link to people who cannot hide revisions
324 - if( $this->mOldRev->getVisibility() || $wgUser->isAllowed( 'deleterevision' ) ) {
325 - if( !$this->mOldRev->userCan( Revision::DELETED_RESTRICTED ) ) {
326 - // If revision was hidden from sysops
327 - $ldel = Xml::tags( 'span', array( 'class' => 'mw-revdelundel-link' ),
328 - '(' . wfMsgHtml( 'rev-delundel' ) . ')' );
329 - } else {
330 - $query = array(
331 - 'type' => 'revision',
332 - 'target' => $this->mOldRev->mTitle->getPrefixedDbkey(),
333 - 'ids' => $this->mOldRev->getId()
334 - );
335 - $ldel = $sk->revDeleteLink( $query, $this->mOldRev->isDeleted( Revision::DELETED_RESTRICTED ) );
336 - }
337 - $ldel = "&nbsp;&nbsp;&nbsp;$ldel ";
338 - }
339 - // Don't show useless link to people who cannot hide revisions
340 - if( $this->mNewRev->getVisibility() || $wgUser->isAllowed( 'deleterevision' ) ) {
341 - if( !$this->mNewRev->userCan( Revision::DELETED_RESTRICTED ) ) {
342 - // If revision was hidden from sysops
343 - $rdel = Xml::tags( 'span', array( 'class'=>'mw-revdelundel-link' ), '('.wfMsgHtml( 'rev-delundel' ).')' );
344 - } else {
345 - $query = array(
346 - 'type' => 'revision',
347 - 'target' => $this->mNewRev->mTitle->getPrefixedDbkey(),
348 - 'ids' => $this->mNewRev->getId()
349 - );
350 - $rdel = $sk->revDeleteLink( $query, $this->mNewRev->isDeleted( Revision::DELETED_RESTRICTED ) );
351 - }
352 - $rdel = "&nbsp;&nbsp;&nbsp;$rdel ";
353 - }
354 - }
355 -
356 - $oldHeader = '<div id="mw-diff-otitle1"><strong>'.$this->mOldtitle.'</strong></div>' .
357 - '<div id="mw-diff-otitle2">' . $sk->revUserTools( $this->mOldRev, !$this->unhide ) . "</div>" .
358 - '<div id="mw-diff-otitle3">' . $oldminor . $sk->revComment( $this->mOldRev, !$diffOnly, !$this->unhide ).$ldel."</div>" .
359 - '<div id="mw-diff-otitle4">' . $prevlink .'</div>';
360 - $newHeader = '<div id="mw-diff-ntitle1"><strong>'.$this->mNewtitle.'</strong></div>' .
361 - '<div id="mw-diff-ntitle2">' . $sk->revUserTools( $this->mNewRev, !$this->unhide ) . " $rollback</div>" .
362 - '<div id="mw-diff-ntitle3">' . $newminor . $sk->revComment( $this->mNewRev, !$diffOnly, !$this->unhide ).$rdel."</div>" .
363 - '<div id="mw-diff-ntitle4">' . $nextlink . $patrol . '</div>';
364 -
365 - # Check if this user can see the revisions
366 - $allowed = $this->mOldRev->userCan(Revision::DELETED_TEXT)
367 - && $this->mNewRev->userCan(Revision::DELETED_TEXT);
368 - # Check if one of the revisions is deleted/suppressed
369 - $deleted = $suppressed = false;
370 - if( $this->mOldRev->isDeleted(Revision::DELETED_TEXT) ) {
371 - $deleted = true; // old revisions text is hidden
372 - if( $this->mOldRev->isDeleted(Revision::DELETED_RESTRICTED) )
373 - $suppressed = true; // also suppressed
374 - }
375 - if( $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
376 - $deleted = true; // new revisions text is hidden
377 - if( $this->mNewRev->isDeleted(Revision::DELETED_RESTRICTED) )
378 - $suppressed = true; // also suppressed
379 - }
380 - # Output the diff if allowed...
381 - if( $deleted && (!$this->unhide || !$allowed) ) {
382 - $this->showDiffStyle();
383 - $multi = $this->getMultiNotice();
384 - $wgOut->addHTML( $this->addHeader( '', $oldHeader, $newHeader, $multi ) );
385 - if( !$allowed ) {
386 - # Give explanation for why revision is not visible
387 - $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n",
388 - array( 'rev-deleted-no-diff' ) );
389 - } else {
390 - # Give explanation and add a link to view the diff...
391 - $link = $this->mTitle->getFullUrl( array(
392 - 'diff' => $this->mNewid,
393 - 'oldid' => $this->mOldid,
394 - 'unhide' => 1
395 - ) );
396 - $msg = $suppressed ? 'rev-suppressed-unhide-diff' : 'rev-deleted-unhide-diff';
397 - $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", array( $msg, $link ) );
398 - }
399 - } else if( $wgEnableHtmlDiff && $this->htmldiff ) {
400 - $multi = $this->getMultiNotice();
401 - $wgOut->addHTML( '<div class="diff-switchtype">' . $sk->link(
402 - $this->mTitle,
403 - wfMsgHtml( 'wikicodecomparison' ),
404 - array(
405 - 'id' => 'differences-switchtype'
406 - ),
407 - array(
408 - 'diff' => $this->mNewid,
409 - 'oldid' => $this->mOldid,
410 - 'htmldiff' => 0
411 - ),
412 - array(
413 - 'known',
414 - 'noclasses'
415 - )
416 - ) . '</div>');
417 - $wgOut->addHTML( $this->addHeader( '', $oldHeader, $newHeader, $multi ) );
418 - $this->renderHtmlDiff();
419 - } else {
420 - if( $wgEnableHtmlDiff ) {
421 - $wgOut->addHTML( '<div class="diff-switchtype">' . $sk->link(
422 - $this->mTitle,
423 - wfMsgHtml( 'visualcomparison' ),
424 - array(
425 - 'id' => 'differences-switchtype'
426 - ),
427 - array(
428 - 'diff' => $this->mNewid,
429 - 'oldid' => $this->mOldid,
430 - 'htmldiff' => 1
431 - ),
432 - array(
433 - 'known',
434 - 'noclasses'
435 - )
436 - ) . '</div>');
437 - }
438 - $this->showDiff( $oldHeader, $newHeader );
439 - if( !$diffOnly ) {
440 - $this->renderNewRevision();
441 - }
442 - }
443 - wfProfileOut( __METHOD__ );
444 - }
445 -
446 - /**
447 - * Show the new revision of the page.
448 - */
449 - function renderNewRevision() {
450 - global $wgOut, $wgUser;
451 - wfProfileIn( __METHOD__ );
452 -
453 - $wgOut->addHTML( "<hr /><h2>{$this->mPagetitle}</h2>\n" );
454 - # Add deleted rev tag if needed
455 - if( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
456 - $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-permission' );
457 - } else if( $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
458 - $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-view' );
459 - }
460 -
461 - if( !$this->mNewRev->isCurrent() ) {
462 - $oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false );
463 - }
464 -
465 - $this->loadNewText();
466 - if( is_object( $this->mNewRev ) ) {
467 - $wgOut->setRevisionId( $this->mNewRev->getId() );
468 - }
469 -
470 - if( $this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage() ) {
471 - // Stolen from Article::view --AG 2007-10-11
472 - // Give hooks a chance to customise the output
473 - if( wfRunHooks( 'ShowRawCssJs', array( $this->mNewtext, $this->mTitle, $wgOut ) ) ) {
474 - // Wrap the whole lot in a <pre> and don't parse
475 - $m = array();
476 - preg_match( '!\.(css|js)$!u', $this->mTitle->getText(), $m );
477 - $wgOut->addHTML( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" );
478 - $wgOut->addHTML( htmlspecialchars( $this->mNewtext ) );
479 - $wgOut->addHTML( "\n</pre>\n" );
480 - }
481 - } else {
482 - $wgOut->addWikiTextTidy( $this->mNewtext );
483 - }
484 -
485 - if( is_object( $this->mNewRev ) && !$this->mNewRev->isCurrent() ) {
486 - $wgOut->parserOptions()->setEditSection( $oldEditSectionSetting );
487 - }
488 - # Add redundant patrol link on bottom...
489 - if( $this->mRcidMarkPatrolled && $this->mTitle->quickUserCan('patrol') ) {
490 - $sk = $wgUser->getSkin();
491 - $wgOut->addHTML(
492 - "<div class='patrollink'>[" . $sk->link(
493 - $this->mTitle,
494 - wfMsgHtml( 'markaspatrolleddiff' ),
495 - array(),
496 - array(
497 - 'action' => 'markpatrolled',
498 - 'rcid' => $this->mRcidMarkPatrolled
499 - )
500 - ) . ']</div>'
501 - );
502 - }
503 -
504 - wfProfileOut( __METHOD__ );
505 - }
506 -
507 -
508 - function renderHtmlDiff() {
509 - global $wgOut, $wgParser, $wgDebugComments;
510 - wfProfileIn( __METHOD__ );
511 -
512 - $this->showDiffStyle();
513 -
514 - $wgOut->addHTML( '<h2>'.wfMsgHtml( 'visual-comparison' )."</h2>\n" );
515 - #add deleted rev tag if needed
516 - if( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
517 - $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-permission' );
518 - } else if( $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
519 - $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-view' );
520 - }
521 -
522 - if( !$this->mNewRev->isCurrent() ) {
523 - $oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false );
524 - }
525 -
526 - $this->loadText();
527 -
528 - // Old revision
529 - if( is_object( $this->mOldRev ) ) {
530 - $wgOut->setRevisionId( $this->mOldRev->getId() );
531 - }
532 -
533 - $popts = $wgOut->parserOptions();
534 - $oldTidy = $popts->setTidy( true );
535 - $popts->setEditSection( false );
536 -
537 - $parserOutput = $wgParser->parse( $this->mOldtext, $this->getTitle(), $popts, true, true, $wgOut->getRevisionId() );
538 - $popts->setTidy( $oldTidy );
539 -
540 - //only for new?
541 - //$wgOut->addParserOutputNoText( $parserOutput );
542 - $oldHtml = $parserOutput->getText();
543 - wfRunHooks( 'OutputPageBeforeHTML', array( &$wgOut, &$oldHtml ) );
544 -
545 - // New revision
546 - if( is_object( $this->mNewRev ) ) {
547 - $wgOut->setRevisionId( $this->mNewRev->getId() );
548 - }
549 -
550 - $popts = $wgOut->parserOptions();
551 - $oldTidy = $popts->setTidy( true );
552 -
553 - $parserOutput = $wgParser->parse( $this->mNewtext, $this->getTitle(), $popts, true, true, $wgOut->getRevisionId() );
554 - $popts->setTidy( $oldTidy );
555 -
556 - $wgOut->addParserOutputNoText( $parserOutput );
557 - $newHtml = $parserOutput->getText();
558 - wfRunHooks( 'OutputPageBeforeHTML', array( &$wgOut, &$newHtml ) );
559 -
560 - unset($parserOutput, $popts);
561 -
562 - $differ = new HTMLDiffer(new DelegatingContentHandler($wgOut));
563 - $differ->htmlDiff($oldHtml, $newHtml);
564 - if ( $wgDebugComments ) {
565 - $wgOut->addHTML( "\n<!-- HtmlDiff Debug Output:\n" . HTMLDiffer::getDebugOutput() . " End Debug -->" );
566 - }
567 -
568 - wfProfileOut( __METHOD__ );
569 - }
570 -
571 - /**
572 - * Show the first revision of an article. Uses normal diff headers in
573 - * contrast to normal "old revision" display style.
574 - */
575 - function showFirstRevision() {
576 - global $wgOut, $wgUser;
577 - wfProfileIn( __METHOD__ );
578 -
579 - # Get article text from the DB
580 - #
581 - if ( ! $this->loadNewText() ) {
582 - $t = $this->mTitle->getPrefixedText();
583 - $d = wfMsgExt( 'missingarticle-diff', array( 'escape' ), $this->mOldid, $this->mNewid );
584 - $wgOut->setPagetitle( wfMsg( 'errorpagetitle' ) );
585 - $wgOut->addWikiMsg( 'missing-article', "<nowiki>$t</nowiki>", $d );
586 - wfProfileOut( __METHOD__ );
587 - return;
588 - }
589 - if ( $this->mNewRev->isCurrent() ) {
590 - $wgOut->setArticleFlag( true );
591 - }
592 -
593 - # Check if user is allowed to look at this page. If not, bail out.
594 - #
595 - if ( !$this->mTitle->userCanRead() ) {
596 - $wgOut->loginToUse();
597 - $wgOut->output();
598 - wfProfileOut( __METHOD__ );
599 - throw new MWException("Permission Error: you do not have access to view this page");
600 - }
601 -
602 - # Prepare the header box
603 - #
604 - $sk = $wgUser->getSkin();
605 -
606 - $next = $this->mTitle->getNextRevisionID( $this->mNewid );
607 - if( !$next ) {
608 - $nextlink = '';
609 - } else {
610 - $nextlink = '<br/>' . $sk->link(
611 - $this->mTitle,
612 - wfMsgHtml( 'nextdiff' ),
613 - array(
614 - 'id' => 'differences-nextlink'
615 - ),
616 - array(
617 - 'diff' => 'next',
618 - 'oldid' => $this->mNewid,
619 - $this->htmlDiffArgument()
620 - ),
621 - array(
622 - 'known',
623 - 'noclasses'
624 - )
625 - );
626 - }
627 - $header = "<div class=\"firstrevisionheader\" style=\"text-align: center\">" .
628 - $sk->revUserTools( $this->mNewRev ) . "<br/>" . $sk->revComment( $this->mNewRev ) . $nextlink . "</div>\n";
629 -
630 - $wgOut->addHTML( $header );
631 -
632 - $wgOut->setSubtitle( wfMsgExt( 'difference', array( 'parseinline' ) ) );
633 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
634 -
635 - wfProfileOut( __METHOD__ );
636 - }
637 -
638 - function htmlDiffArgument(){
639 - global $wgEnableHtmlDiff;
640 - if($wgEnableHtmlDiff){
641 - if($this->htmldiff){
642 - return array( 'htmldiff' => 1 );
643 - }else{
644 - return array( 'htmldiff' => 0 );
645 - }
646 - }else{
647 - return array();
648 - }
649 - }
650 -
651 - /**
652 - * Get the diff text, send it to $wgOut
653 - * Returns false if the diff could not be generated, otherwise returns true
654 - */
655 - function showDiff( $otitle, $ntitle ) {
656 - global $wgOut;
657 - $diff = $this->getDiff( $otitle, $ntitle );
658 - if ( $diff === false ) {
659 - $wgOut->addWikiMsg( 'missing-article', "<nowiki>(fixme, bug)</nowiki>", '' );
660 - return false;
661 - } else {
662 - $this->showDiffStyle();
663 - $wgOut->addHTML( $diff );
664 - return true;
665 - }
666 - }
667 -
668 - /**
669 - * Add style sheets and supporting JS for diff display.
670 - */
671 - function showDiffStyle() {
672 - global $wgStylePath, $wgStyleVersion, $wgOut;
673 - $wgOut->addStyle( 'common/diff.css' );
674 -
675 - // JS is needed to detect old versions of Mozilla to work around an annoyance bug.
676 - $wgOut->addScript( "<script type=\"text/javascript\" src=\"$wgStylePath/common/diff.js?$wgStyleVersion\"></script>" );
677 - }
678 -
679 - /**
680 - * Get complete diff table, including header
681 - *
682 - * @param Title $otitle Old title
683 - * @param Title $ntitle New title
684 - * @return mixed
685 - */
686 - function getDiff( $otitle, $ntitle ) {
687 - $body = $this->getDiffBody();
688 - if ( $body === false ) {
689 - return false;
690 - } else {
691 - $multi = $this->getMultiNotice();
692 - return $this->addHeader( $body, $otitle, $ntitle, $multi );
693 - }
694 - }
695 -
696 - /**
697 - * Get the diff table body, without header
698 - *
699 - * @return mixed
700 - */
701 - function getDiffBody() {
702 - global $wgMemc;
703 - wfProfileIn( __METHOD__ );
704 - $this->mCacheHit = true;
705 - // Check if the diff should be hidden from this user
706 - if ( !$this->loadRevisionData() )
707 - return '';
708 - if ( $this->mOldRev && !$this->mOldRev->userCan(Revision::DELETED_TEXT) ) {
709 - return '';
710 - } else if ( $this->mNewRev && !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
711 - return '';
712 - } else if ( $this->mOldRev && $this->mNewRev && $this->mOldRev->getID() == $this->mNewRev->getID() ) {
713 - return '';
714 - }
715 - // Cacheable?
716 - $key = false;
717 - if ( $this->mOldid && $this->mNewid ) {
718 - $key = wfMemcKey( 'diff', 'version', MW_DIFF_VERSION, 'oldid', $this->mOldid, 'newid', $this->mNewid );
719 - // Try cache
720 - if ( !$this->mRefreshCache ) {
721 - $difftext = $wgMemc->get( $key );
722 - if ( $difftext ) {
723 - wfIncrStats( 'diff_cache_hit' );
724 - $difftext = $this->localiseLineNumbers( $difftext );
725 - $difftext .= "\n<!-- diff cache key $key -->\n";
726 - wfProfileOut( __METHOD__ );
727 - return $difftext;
728 - }
729 - } // don't try to load but save the result
730 - }
731 - $this->mCacheHit = false;
732 -
733 - // Loadtext is permission safe, this just clears out the diff
734 - if ( !$this->loadText() ) {
735 - wfProfileOut( __METHOD__ );
736 - return false;
737 - }
738 -
739 - $difftext = $this->generateDiffBody( $this->mOldtext, $this->mNewtext );
740 -
741 - // Save to cache for 7 days
742 - if ( !wfRunHooks( 'AbortDiffCache', array( &$this ) ) ) {
743 - wfIncrStats( 'diff_uncacheable' );
744 - } else if ( $key !== false && $difftext !== false ) {
745 - wfIncrStats( 'diff_cache_miss' );
746 - $wgMemc->set( $key, $difftext, 7*86400 );
747 - } else {
748 - wfIncrStats( 'diff_uncacheable' );
749 - }
750 - // Replace line numbers with the text in the user's language
751 - if ( $difftext !== false ) {
752 - $difftext = $this->localiseLineNumbers( $difftext );
753 - }
754 - wfProfileOut( __METHOD__ );
755 - return $difftext;
756 - }
757 -
758 - /**
759 - * Make sure the proper modules are loaded before we try to
760 - * make the diff
761 - */
762 - private function initDiffEngines() {
763 - global $wgExternalDiffEngine;
764 - if ( $wgExternalDiffEngine == 'wikidiff' && !function_exists( 'wikidiff_do_diff' ) ) {
765 - wfProfileIn( __METHOD__ . '-php_wikidiff.so' );
766 - wfSuppressWarnings();
767 - dl( 'php_wikidiff.so' );
768 - wfRestoreWarnings();
769 - wfProfileOut( __METHOD__ . '-php_wikidiff.so' );
770 - }
771 - else if ( $wgExternalDiffEngine == 'wikidiff2' && !function_exists( 'wikidiff2_do_diff' ) ) {
772 - wfProfileIn( __METHOD__ . '-php_wikidiff2.so' );
773 - wfSuppressWarnings();
774 - dl( 'php_wikidiff2.so' );
775 - wfRestoreWarnings();
776 - wfProfileOut( __METHOD__ . '-php_wikidiff2.so' );
777 - }
778 - }
779 -
780 - /**
781 - * Generate a diff, no caching
782 - * $otext and $ntext must be already segmented
783 - */
784 - function generateDiffBody( $otext, $ntext ) {
785 - global $wgExternalDiffEngine, $wgContLang;
786 -
787 - $otext = str_replace( "\r\n", "\n", $otext );
788 - $ntext = str_replace( "\r\n", "\n", $ntext );
789 -
790 - $this->initDiffEngines();
791 -
792 - if ( $wgExternalDiffEngine == 'wikidiff' && function_exists( 'wikidiff_do_diff' ) ) {
793 - # For historical reasons, external diff engine expects
794 - # input text to be HTML-escaped already
795 - $otext = htmlspecialchars ( $wgContLang->segmentForDiff( $otext ) );
796 - $ntext = htmlspecialchars ( $wgContLang->segmentForDiff( $ntext ) );
797 - return $wgContLang->unsegementForDiff( wikidiff_do_diff( $otext, $ntext, 2 ) ) .
798 - $this->debug( 'wikidiff1' );
799 - }
800 -
801 - if ( $wgExternalDiffEngine == 'wikidiff2' && function_exists( 'wikidiff2_do_diff' ) ) {
802 - # Better external diff engine, the 2 may some day be dropped
803 - # This one does the escaping and segmenting itself
804 - wfProfileIn( 'wikidiff2_do_diff' );
805 - $text = wikidiff2_do_diff( $otext, $ntext, 2 );
806 - $text .= $this->debug( 'wikidiff2' );
807 - wfProfileOut( 'wikidiff2_do_diff' );
808 - return $text;
809 - }
810 - if ( $wgExternalDiffEngine != 'wikidiff3' && $wgExternalDiffEngine !== false ) {
811 - # Diff via the shell
812 - global $wgTmpDirectory;
813 - $tempName1 = tempnam( $wgTmpDirectory, 'diff_' );
814 - $tempName2 = tempnam( $wgTmpDirectory, 'diff_' );
815 -
816 - $tempFile1 = fopen( $tempName1, "w" );
817 - if ( !$tempFile1 ) {
818 - wfProfileOut( __METHOD__ );
819 - return false;
820 - }
821 - $tempFile2 = fopen( $tempName2, "w" );
822 - if ( !$tempFile2 ) {
823 - wfProfileOut( __METHOD__ );
824 - return false;
825 - }
826 - fwrite( $tempFile1, $otext );
827 - fwrite( $tempFile2, $ntext );
828 - fclose( $tempFile1 );
829 - fclose( $tempFile2 );
830 - $cmd = wfEscapeShellArg( $wgExternalDiffEngine, $tempName1, $tempName2 );
831 - wfProfileIn( __METHOD__ . "-shellexec" );
832 - $difftext = wfShellExec( $cmd );
833 - $difftext .= $this->debug( "external $wgExternalDiffEngine" );
834 - wfProfileOut( __METHOD__ . "-shellexec" );
835 - unlink( $tempName1 );
836 - unlink( $tempName2 );
837 - return $difftext;
838 - }
839 -
840 - # Native PHP diff
841 - $ota = explode( "\n", $wgContLang->segmentForDiff( $otext ) );
842 - $nta = explode( "\n", $wgContLang->segmentForDiff( $ntext ) );
843 - $diffs = new Diff( $ota, $nta );
844 - $formatter = new TableDiffFormatter();
845 - return $wgContLang->unsegmentForDiff( $formatter->format( $diffs ) ) .
846 - $this->debug();
847 - }
848 -
849 - /**
850 - * Generate a debug comment indicating diff generating time,
851 - * server node, and generator backend.
852 - */
853 - protected function debug( $generator="internal" ) {
854 - global $wgShowHostnames;
855 - if ( !$this->enableDebugComment ) {
856 - return '';
857 - }
858 - $data = array( $generator );
859 - if( $wgShowHostnames ) {
860 - $data[] = wfHostname();
861 - }
862 - $data[] = wfTimestamp( TS_DB );
863 - return "<!-- diff generator: " .
864 - implode( " ",
865 - array_map(
866 - "htmlspecialchars",
867 - $data ) ) .
868 - " -->\n";
869 - }
870 -
871 - /**
872 - * Replace line numbers with the text in the user's language
873 - */
874 - function localiseLineNumbers( $text ) {
875 - return preg_replace_callback( '/<!--LINE (\d+)-->/',
876 - array( &$this, 'localiseLineNumbersCb' ), $text );
877 - }
878 -
879 - function localiseLineNumbersCb( $matches ) {
880 - global $wgLang;
881 - if ( $matches[1] === '1' && $this->mReducedLineNumbers ) return '';
882 - return wfMsgExt( 'lineno', 'escape', $wgLang->formatNum( $matches[1] ) );
883 - }
884 -
885 -
886 - /**
887 - * If there are revisions between the ones being compared, return a note saying so.
888 - */
889 - function getMultiNotice() {
890 - if ( !is_object($this->mOldRev) || !is_object($this->mNewRev) )
891 - return '';
892 -
893 - if( !$this->mOldPage->equals( $this->mNewPage ) ) {
894 - // Comparing two different pages? Count would be meaningless.
895 - return '';
896 - }
897 -
898 - $oldid = $this->mOldRev->getId();
899 - $newid = $this->mNewRev->getId();
900 - if ( $oldid > $newid ) {
901 - $tmp = $oldid; $oldid = $newid; $newid = $tmp;
902 - }
903 -
904 - $n = $this->mTitle->countRevisionsBetween( $oldid, $newid );
905 - if ( !$n )
906 - return '';
907 -
908 - return wfMsgExt( 'diff-multi', array( 'parseinline' ), $n );
909 - }
910 -
911 -
912 - /**
913 - * Add the header to a diff body
914 - */
915 - static function addHeader( $diff, $otitle, $ntitle, $multi = '' ) {
916 - $colspan = 1;
917 - $header = "<table class='diff'>";
918 - if( $diff ) { // Safari/Chrome show broken output if cols not used
919 - $header .= "
920 - <col class='diff-marker' />
921 - <col class='diff-content' />
922 - <col class='diff-marker' />
923 - <col class='diff-content' />";
924 - $colspan = 2;
925 - }
926 - $header .= "
927 - <tr valign='top'>
928 - <td colspan='$colspan' class='diff-otitle'>{$otitle}</td>
929 - <td colspan='$colspan' class='diff-ntitle'>{$ntitle}</td>
930 - </tr>";
931 -
932 - if ( $multi != '' )
933 - $header .= "<tr><td colspan='4' align='center' class='diff-multi'>{$multi}</td></tr>";
934 -
935 - return $header . $diff . "</table>";
936 - }
937 -
938 - /**
939 - * Use specified text instead of loading from the database
940 - */
941 - function setText( $oldText, $newText ) {
942 - $this->mOldtext = $oldText;
943 - $this->mNewtext = $newText;
944 - $this->mTextLoaded = 2;
945 - $this->mRevisionsLoaded = true;
946 - }
947 -
948 - /**
949 - * Load revision metadata for the specified articles. If newid is 0, then compare
950 - * the old article in oldid to the current article; if oldid is 0, then
951 - * compare the current article to the immediately previous one (ignoring the
952 - * value of newid).
953 - *
954 - * If oldid is false, leave the corresponding revision object set
955 - * to false. This is impossible via ordinary user input, and is provided for
956 - * API convenience.
957 - */
958 - function loadRevisionData() {
959 - global $wgLang, $wgUser;
960 - if ( $this->mRevisionsLoaded ) {
961 - return true;
962 - } else {
963 - // Whether it succeeds or fails, we don't want to try again
964 - $this->mRevisionsLoaded = true;
965 - }
966 -
967 - // Load the new revision object
968 - $this->mNewRev = $this->mNewid
969 - ? Revision::newFromId( $this->mNewid )
970 - : Revision::newFromTitle( $this->mTitle );
971 - if( !$this->mNewRev instanceof Revision )
972 - return false;
973 -
974 - // Update the new revision ID in case it was 0 (makes life easier doing UI stuff)
975 - $this->mNewid = $this->mNewRev->getId();
976 -
977 - // Check if page is editable
978 - $editable = $this->mNewRev->getTitle()->userCan( 'edit' );
979 -
980 - // Set assorted variables
981 - $timestamp = $wgLang->timeanddate( $this->mNewRev->getTimestamp(), true );
982 - $dateofrev = $wgLang->date( $this->mNewRev->getTimestamp(), true );
983 - $timeofrev = $wgLang->time( $this->mNewRev->getTimestamp(), true );
984 - $this->mNewPage = $this->mNewRev->getTitle();
985 - if( $this->mNewRev->isCurrent() ) {
986 - $newLink = $this->mNewPage->escapeLocalUrl( array(
987 - 'oldid' => $this->mNewid
988 - ) );
989 - $this->mPagetitle = htmlspecialchars( wfMsg(
990 - 'currentrev-asof',
991 - $timestamp,
992 - $dateofrev,
993 - $timeofrev
994 - ) );
995 - $newEdit = $this->mNewPage->escapeLocalUrl( array(
996 - 'action' => 'edit'
997 - ) );
998 -
999 - $this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a>";
1000 - $this->mNewtitle .= " (<a href='$newEdit'>" . wfMsgHtml( $editable ? 'editold' : 'viewsourceold' ) . "</a>)";
1001 - } else {
1002 - $newLink = $this->mNewPage->escapeLocalUrl( array(
1003 - 'oldid' => $this->mNewid
1004 - ) );
1005 - $newEdit = $this->mNewPage->escapeLocalUrl( array(
1006 - 'action' => 'edit',
1007 - 'oldid' => $this->mNewid
1008 - ) );
1009 - $this->mPagetitle = htmlspecialchars( wfMsg(
1010 - 'revisionasof',
1011 - $timestamp,
1012 - $dateofrev,
1013 - $timeofrev
1014 - ) );
1015 -
1016 - $this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a>";
1017 - $this->mNewtitle .= " (<a href='$newEdit'>" . wfMsgHtml( $editable ? 'editold' : 'viewsourceold' ) . "</a>)";
1018 - }
1019 - if( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
1020 - $this->mNewtitle = "<span class='history-deleted'>{$this->mPagetitle}</span>";
1021 - } else if ( $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
1022 - $this->mNewtitle = "<span class='history-deleted'>{$this->mNewtitle}</span>";
1023 - }
1024 -
1025 - // Load the old revision object
1026 - $this->mOldRev = false;
1027 - if( $this->mOldid ) {
1028 - $this->mOldRev = Revision::newFromId( $this->mOldid );
1029 - } elseif ( $this->mOldid === 0 ) {
1030 - $rev = $this->mNewRev->getPrevious();
1031 - if( $rev ) {
1032 - $this->mOldid = $rev->getId();
1033 - $this->mOldRev = $rev;
1034 - } else {
1035 - // No previous revision; mark to show as first-version only.
1036 - $this->mOldid = false;
1037 - $this->mOldRev = false;
1038 - }
1039 - }/* elseif ( $this->mOldid === false ) leave mOldRev false; */
1040 -
1041 - if( is_null( $this->mOldRev ) ) {
1042 - return false;
1043 - }
1044 -
1045 - if ( $this->mOldRev ) {
1046 - $this->mOldPage = $this->mOldRev->getTitle();
1047 -
1048 - $t = $wgLang->timeanddate( $this->mOldRev->getTimestamp(), true );
1049 - $dateofrev = $wgLang->date( $this->mOldRev->getTimestamp(), true );
1050 - $timeofrev = $wgLang->time( $this->mOldRev->getTimestamp(), true );
1051 - $oldLink = $this->mOldPage->escapeLocalUrl( array(
1052 - 'oldid' => $this->mOldid
1053 - ) );
1054 - $oldEdit = $this->mOldPage->escapeLocalUrl( array(
1055 - 'action' => 'edit',
1056 - 'oldid' => $this->mOldid
1057 - ) );
1058 - $this->mOldPagetitle = htmlspecialchars( wfMsg( 'revisionasof', $t, $dateofrev, $timeofrev ) );
1059 -
1060 - $this->mOldtitle = "<a href='$oldLink'>{$this->mOldPagetitle}</a>"
1061 - . " (<a href='$oldEdit'>" . wfMsgHtml( $editable ? 'editold' : 'viewsourceold' ) . "</a>)";
1062 - // Add an "undo" link
1063 - $newUndo = $this->mNewPage->escapeLocalUrl( array(
1064 - 'action' => 'edit',
1065 - 'undoafter' => $this->mOldid,
1066 - 'undo' => $this->mNewid
1067 - ) );
1068 - $htmlLink = htmlspecialchars( wfMsg( 'editundo' ) );
1069 - $htmlTitle = $wgUser->getSkin()->tooltip( 'undo' );
1070 - if( $editable && !$this->mOldRev->isDeleted( Revision::DELETED_TEXT ) && !$this->mNewRev->isDeleted( Revision::DELETED_TEXT ) ) {
1071 - $this->mNewtitle .= " (<a href='$newUndo' $htmlTitle>" . $htmlLink . "</a>)";
1072 - }
1073 -
1074 - if( !$this->mOldRev->userCan( Revision::DELETED_TEXT ) ) {
1075 - $this->mOldtitle = '<span class="history-deleted">' . $this->mOldPagetitle . '</span>';
1076 - } else if( $this->mOldRev->isDeleted( Revision::DELETED_TEXT ) ) {
1077 - $this->mOldtitle = '<span class="history-deleted">' . $this->mOldtitle . '</span>';
1078 - }
1079 - }
1080 -
1081 - return true;
1082 - }
1083 -
1084 - /**
1085 - * Load the text of the revisions, as well as revision data.
1086 - */
1087 - function loadText() {
1088 - if ( $this->mTextLoaded == 2 ) {
1089 - return true;
1090 - } else {
1091 - // Whether it succeeds or fails, we don't want to try again
1092 - $this->mTextLoaded = 2;
1093 - }
1094 -
1095 - if ( !$this->loadRevisionData() ) {
1096 - return false;
1097 - }
1098 - if ( $this->mOldRev ) {
1099 - $this->mOldtext = $this->mOldRev->getText( Revision::FOR_THIS_USER );
1100 - if ( $this->mOldtext === false ) {
1101 - return false;
1102 - }
1103 - }
1104 - if ( $this->mNewRev ) {
1105 - $this->mNewtext = $this->mNewRev->getText( Revision::FOR_THIS_USER );
1106 - if ( $this->mNewtext === false ) {
1107 - return false;
1108 - }
1109 - }
1110 - return true;
1111 - }
1112 -
1113 - /**
1114 - * Load the text of the new revision, not the old one
1115 - */
1116 - function loadNewText() {
1117 - if ( $this->mTextLoaded >= 1 ) {
1118 - return true;
1119 - } else {
1120 - $this->mTextLoaded = 1;
1121 - }
1122 - if ( !$this->loadRevisionData() ) {
1123 - return false;
1124 - }
1125 - $this->mNewtext = $this->mNewRev->getText( Revision::FOR_THIS_USER );
1126 - return true;
1127 - }
1128 -}
 2+<?php
 3+/**
 4+ * @defgroup DifferenceEngine DifferenceEngine
 5+ */
 6+
 7+/**
 8+ * Constant to indicate diff cache compatibility.
 9+ * Bump this when changing the diff formatting in a way that
 10+ * fixes important bugs or such to force cached diff views to
 11+ * clear.
 12+ */
 13+define( 'MW_DIFF_VERSION', '1.11a' );
 14+
 15+/**
 16+ * @todo document
 17+ * @ingroup DifferenceEngine
 18+ */
 19+class DifferenceEngine {
 20+ /**#@+
 21+ * @private
 22+ */
 23+ var $mOldid, $mNewid, $mTitle;
 24+ var $mOldtitle, $mNewtitle, $mPagetitle;
 25+ var $mOldtext, $mNewtext;
 26+ var $mOldPage, $mNewPage;
 27+ var $mRcidMarkPatrolled;
 28+ var $mOldRev, $mNewRev;
 29+ var $mRevisionsLoaded = false; // Have the revisions been loaded
 30+ var $mTextLoaded = 0; // How many text blobs have been loaded, 0, 1 or 2?
 31+ var $mCacheHit = false; // Was the diff fetched from cache?
 32+ var $htmldiff;
 33+
 34+ /**
 35+ * Set this to true to add debug info to the HTML output.
 36+ * Warning: this may cause RSS readers to spuriously mark articles as "new"
 37+ * (bug 20601)
 38+ */
 39+ var $enableDebugComment = false;
 40+
 41+ // If true, line X is not displayed when X is 1, for example to increase
 42+ // readability and conserve space with many small diffs.
 43+ protected $mReducedLineNumbers = false;
 44+
 45+ protected $unhide = false;
 46+ /**#@-*/
 47+
 48+ /**
 49+ * Constructor
 50+ * @param $titleObj Title object that the diff is associated with
 51+ * @param $old Integer: old ID we want to show and diff with.
 52+ * @param $new String: either 'prev' or 'next'.
 53+ * @param $rcid Integer: ??? FIXME (default 0)
 54+ * @param $refreshCache boolean If set, refreshes the diff cache
 55+ * @param $htmldiff boolean If set, output using HTMLDiff instead of raw wikicode diff
 56+ * @param $unhide boolean If set, allow viewing deleted revs
 57+ */
 58+ function __construct( $titleObj = null, $old = 0, $new = 0, $rcid = 0,
 59+ $refreshCache = false, $htmldiff = false, $unhide = false )
 60+ {
 61+ if ( $titleObj ) {
 62+ $this->mTitle = $titleObj;
 63+ } else {
 64+ global $wgTitle;
 65+ $this->mTitle = $wgTitle;
 66+ }
 67+ wfDebug("DifferenceEngine old '$old' new '$new' rcid '$rcid'\n");
 68+
 69+ if ( 'prev' === $new ) {
 70+ # Show diff between revision $old and the previous one.
 71+ # Get previous one from DB.
 72+ $this->mNewid = intval($old);
 73+ $this->mOldid = $this->mTitle->getPreviousRevisionID( $this->mNewid );
 74+ } elseif ( 'next' === $new ) {
 75+ # Show diff between revision $old and the next one.
 76+ # Get next one from DB.
 77+ $this->mOldid = intval($old);
 78+ $this->mNewid = $this->mTitle->getNextRevisionID( $this->mOldid );
 79+ if ( false === $this->mNewid ) {
 80+ # if no result, NewId points to the newest old revision. The only newer
 81+ # revision is cur, which is "0".
 82+ $this->mNewid = 0;
 83+ }
 84+ } else {
 85+ $this->mOldid = intval($old);
 86+ $this->mNewid = intval($new);
 87+ wfRunHooks( 'NewDifferenceEngine', array(&$titleObj, &$this->mOldid, &$this->mNewid, $old, $new) );
 88+ }
 89+ $this->mRcidMarkPatrolled = intval($rcid); # force it to be an integer
 90+ $this->mRefreshCache = $refreshCache;
 91+ $this->htmldiff = $htmldiff;
 92+ $this->unhide = $unhide;
 93+ }
 94+
 95+ function setReducedLineNumbers( $value = true ) {
 96+ $this->mReducedLineNumbers = $value;
 97+ }
 98+
 99+ function getTitle() {
 100+ return $this->mTitle;
 101+ }
 102+
 103+ function wasCacheHit() {
 104+ return $this->mCacheHit;
 105+ }
 106+
 107+ function getOldid() {
 108+ return $this->mOldid;
 109+ }
 110+
 111+ function getNewid() {
 112+ return $this->mNewid;
 113+ }
 114+
 115+ function showDiffPage( $diffOnly = false ) {
 116+ global $wgUser, $wgOut, $wgUseExternalEditor, $wgUseRCPatrol, $wgEnableHtmlDiff;
 117+ wfProfileIn( __METHOD__ );
 118+
 119+
 120+ # If external diffs are enabled both globally and for the user,
 121+ # we'll use the application/x-external-editor interface to call
 122+ # an external diff tool like kompare, kdiff3, etc.
 123+ if($wgUseExternalEditor && $wgUser->getOption('externaldiff')) {
 124+ global $wgInputEncoding,$wgServer,$wgScript,$wgLang;
 125+ $wgOut->disable();
 126+ header ( "Content-type: application/x-external-editor; charset=".$wgInputEncoding );
 127+ $url1=$this->mTitle->getFullURL( array(
 128+ 'action' => 'raw',
 129+ 'oldid' => $this->mOldid
 130+ ) );
 131+ $url2=$this->mTitle->getFullURL( array(
 132+ 'action' => 'raw',
 133+ 'oldid' => $this->mNewid
 134+ ) );
 135+ $special=$wgLang->getNsText(NS_SPECIAL);
 136+ $control=<<<CONTROL
 137+ [Process]
 138+ Type=Diff text
 139+ Engine=MediaWiki
 140+ Script={$wgServer}{$wgScript}
 141+ Special namespace={$special}
 142+
 143+ [File]
 144+ Extension=wiki
 145+ URL=$url1
 146+
 147+ [File 2]
 148+ Extension=wiki
 149+ URL=$url2
 150+CONTROL;
 151+ echo($control);
 152+ return;
 153+ }
 154+
 155+ $wgOut->setArticleFlag( false );
 156+ if ( !$this->loadRevisionData() ) {
 157+ $t = $this->mTitle->getPrefixedText();
 158+ $d = wfMsgExt( 'missingarticle-diff', array( 'escape' ), $this->mOldid, $this->mNewid );
 159+ $wgOut->setPagetitle( wfMsg( 'errorpagetitle' ) );
 160+ $wgOut->addWikiMsg( 'missing-article', "<nowiki>$t</nowiki>", $d );
 161+ wfProfileOut( __METHOD__ );
 162+ return;
 163+ }
 164+
 165+ wfRunHooks( 'DiffViewHeader', array( $this, $this->mOldRev, $this->mNewRev ) );
 166+
 167+ if ( $this->mNewRev->isCurrent() ) {
 168+ $wgOut->setArticleFlag( true );
 169+ }
 170+
 171+ # mOldid is false if the difference engine is called with a "vague" query for
 172+ # a diff between a version V and its previous version V' AND the version V
 173+ # is the first version of that article. In that case, V' does not exist.
 174+ if ( $this->mOldid === false ) {
 175+ $this->showFirstRevision();
 176+ $this->renderNewRevision(); // should we respect $diffOnly here or not?
 177+ wfProfileOut( __METHOD__ );
 178+ return;
 179+ }
 180+
 181+ $wgOut->suppressQuickbar();
 182+
 183+ $oldTitle = $this->mOldPage->getPrefixedText();
 184+ $newTitle = $this->mNewPage->getPrefixedText();
 185+ if( $oldTitle == $newTitle ) {
 186+ $wgOut->setPageTitle( $newTitle );
 187+ } else {
 188+ $wgOut->setPageTitle( $oldTitle . ', ' . $newTitle );
 189+ }
 190+ $wgOut->setSubtitle( wfMsgExt( 'difference', array( 'parseinline' ) ) );
 191+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 192+
 193+ if ( !$this->mOldPage->userCanRead() || !$this->mNewPage->userCanRead() ) {
 194+ $wgOut->loginToUse();
 195+ $wgOut->output();
 196+ $wgOut->disable();
 197+ wfProfileOut( __METHOD__ );
 198+ return;
 199+ }
 200+
 201+ $sk = $wgUser->getSkin();
 202+
 203+ // Check if page is editable
 204+ $editable = $this->mNewRev->getTitle()->userCan( 'edit' );
 205+ if ( $editable && $this->mNewRev->isCurrent() && $wgUser->isAllowed( 'rollback' ) ) {
 206+ $rollback = '&nbsp;&nbsp;&nbsp;' . $sk->generateRollback( $this->mNewRev );
 207+ } else {
 208+ $rollback = '';
 209+ }
 210+
 211+ // Prepare a change patrol link, if applicable
 212+ if( $wgUseRCPatrol && $this->mTitle->userCan('patrol') ) {
 213+ // If we've been given an explicit change identifier, use it; saves time
 214+ if( $this->mRcidMarkPatrolled ) {
 215+ $rcid = $this->mRcidMarkPatrolled;
 216+ $rc = RecentChange::newFromId( $rcid );
 217+ // Already patrolled?
 218+ $rcid = is_object($rc) && !$rc->getAttribute('rc_patrolled') ? $rcid : 0;
 219+ } else {
 220+ // Look for an unpatrolled change corresponding to this diff
 221+ $db = wfGetDB( DB_SLAVE );
 222+ $change = RecentChange::newFromConds(
 223+ array(
 224+ // Redundant user,timestamp condition so we can use the existing index
 225+ 'rc_user_text' => $this->mNewRev->getRawUserText(),
 226+ 'rc_timestamp' => $db->timestamp( $this->mNewRev->getTimestamp() ),
 227+ 'rc_this_oldid' => $this->mNewid,
 228+ 'rc_last_oldid' => $this->mOldid,
 229+ 'rc_patrolled' => 0
 230+ ),
 231+ __METHOD__
 232+ );
 233+ if( $change instanceof RecentChange ) {
 234+ $rcid = $change->mAttribs['rc_id'];
 235+ $this->mRcidMarkPatrolled = $rcid;
 236+ } else {
 237+ // None found
 238+ $rcid = 0;
 239+ }
 240+ }
 241+ // Build the link
 242+ if( $rcid ) {
 243+ $patrol = ' <span class="patrollink">[' . $sk->link(
 244+ $this->mTitle,
 245+ wfMsgHtml( 'markaspatrolleddiff' ),
 246+ array(),
 247+ array(
 248+ 'action' => 'markpatrolled',
 249+ 'rcid' => $rcid
 250+ ),
 251+ array(
 252+ 'known',
 253+ 'noclasses'
 254+ )
 255+ ) . ']</span>';
 256+ } else {
 257+ $patrol = '';
 258+ }
 259+ } else {
 260+ $patrol = '';
 261+ }
 262+
 263+ # Carry over 'diffonly' param via navigation links
 264+ if( $diffOnly != $wgUser->getBoolOption('diffonly') ) {
 265+ $query['diffonly'] = $diffOnly;
 266+ }
 267+
 268+ $htmldiffarg = $this->htmlDiffArgument();
 269+
 270+ if( $htmldiffarg ) {
 271+ $query['htmldiff'] = $htmldiffarg['htmldiff'];
 272+ }
 273+
 274+ # Make "previous revision link"
 275+ $query['diff'] = 'prev';
 276+ $query['oldid'] = $this->mOldid;
 277+
 278+ $prevlink = $sk->link(
 279+ $this->mTitle,
 280+ wfMsgHtml( 'previousdiff' ),
 281+ array(
 282+ 'id' => 'differences-prevlink'
 283+ ),
 284+ $query,
 285+ array(
 286+ 'known',
 287+ 'noclasses'
 288+ )
 289+ );
 290+ # Make "next revision link"
 291+ $query['diff'] = 'next';
 292+ $query['oldid'] = $this->mNewid;
 293+
 294+ if( $this->mNewRev->isCurrent() ) {
 295+ $nextlink = '&nbsp;';
 296+ } else {
 297+ $nextlink = $sk->link(
 298+ $this->mTitle,
 299+ wfMsgHtml( 'nextdiff' ),
 300+ array(
 301+ 'id' => 'differences-nextlink'
 302+ ),
 303+ $query,
 304+ array(
 305+ 'known',
 306+ 'noclasses'
 307+ )
 308+ );
 309+ }
 310+
 311+ $oldminor = '';
 312+ $newminor = '';
 313+
 314+ if( $this->mOldRev->isMinor() ) {
 315+ $oldminor = ChangesList::flag( 'minor' );
 316+ }
 317+ if( $this->mNewRev->isMinor() ) {
 318+ $newminor = ChangesList::flag( 'minor' );
 319+ }
 320+
 321+ $rdel = ''; $ldel = '';
 322+ if( $wgUser->isAllowed( 'deletedhistory' ) ) {
 323+ // Don't show useless link to people who cannot hide revisions
 324+ if( $this->mOldRev->getVisibility() || $wgUser->isAllowed( 'deleterevision' ) ) {
 325+ if( !$this->mOldRev->userCan( Revision::DELETED_RESTRICTED ) ) {
 326+ // If revision was hidden from sysops
 327+ $ldel = Xml::tags( 'span', array( 'class' => 'mw-revdelundel-link' ),
 328+ '(' . wfMsgHtml( 'rev-delundel' ) . ')' );
 329+ } else {
 330+ $query = array(
 331+ 'type' => 'revision',
 332+ 'target' => $this->mOldRev->mTitle->getPrefixedDbkey(),
 333+ 'ids' => $this->mOldRev->getId()
 334+ );
 335+ $ldel = $sk->revDeleteLink( $query, $this->mOldRev->isDeleted( Revision::DELETED_RESTRICTED ) );
 336+ }
 337+ $ldel = "&nbsp;&nbsp;&nbsp;$ldel ";
 338+ }
 339+ // Don't show useless link to people who cannot hide revisions
 340+ if( $this->mNewRev->getVisibility() || $wgUser->isAllowed( 'deleterevision' ) ) {
 341+ if( !$this->mNewRev->userCan( Revision::DELETED_RESTRICTED ) ) {
 342+ // If revision was hidden from sysops
 343+ $rdel = Xml::tags( 'span', array( 'class'=>'mw-revdelundel-link' ), '('.wfMsgHtml( 'rev-delundel' ).')' );
 344+ } else {
 345+ $query = array(
 346+ 'type' => 'revision',
 347+ 'target' => $this->mNewRev->mTitle->getPrefixedDbkey(),
 348+ 'ids' => $this->mNewRev->getId()
 349+ );
 350+ $rdel = $sk->revDeleteLink( $query, $this->mNewRev->isDeleted( Revision::DELETED_RESTRICTED ) );
 351+ }
 352+ $rdel = "&nbsp;&nbsp;&nbsp;$rdel ";
 353+ }
 354+ }
 355+
 356+ $oldHeader = '<div id="mw-diff-otitle1"><strong>'.$this->mOldtitle.'</strong></div>' .
 357+ '<div id="mw-diff-otitle2">' . $sk->revUserTools( $this->mOldRev, !$this->unhide ) . "</div>" .
 358+ '<div id="mw-diff-otitle3">' . $oldminor . $sk->revComment( $this->mOldRev, !$diffOnly, !$this->unhide ).$ldel."</div>" .
 359+ '<div id="mw-diff-otitle4">' . $prevlink .'</div>';
 360+ $newHeader = '<div id="mw-diff-ntitle1"><strong>'.$this->mNewtitle.'</strong></div>' .
 361+ '<div id="mw-diff-ntitle2">' . $sk->revUserTools( $this->mNewRev, !$this->unhide ) . " $rollback</div>" .
 362+ '<div id="mw-diff-ntitle3">' . $newminor . $sk->revComment( $this->mNewRev, !$diffOnly, !$this->unhide ).$rdel."</div>" .
 363+ '<div id="mw-diff-ntitle4">' . $nextlink . $patrol . '</div>';
 364+
 365+ # Check if this user can see the revisions
 366+ $allowed = $this->mOldRev->userCan(Revision::DELETED_TEXT)
 367+ && $this->mNewRev->userCan(Revision::DELETED_TEXT);
 368+ # Check if one of the revisions is deleted/suppressed
 369+ $deleted = $suppressed = false;
 370+ if( $this->mOldRev->isDeleted(Revision::DELETED_TEXT) ) {
 371+ $deleted = true; // old revisions text is hidden
 372+ if( $this->mOldRev->isDeleted(Revision::DELETED_RESTRICTED) )
 373+ $suppressed = true; // also suppressed
 374+ }
 375+ if( $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
 376+ $deleted = true; // new revisions text is hidden
 377+ if( $this->mNewRev->isDeleted(Revision::DELETED_RESTRICTED) )
 378+ $suppressed = true; // also suppressed
 379+ }
 380+ # Output the diff if allowed...
 381+ if( $deleted && (!$this->unhide || !$allowed) ) {
 382+ $this->showDiffStyle();
 383+ $multi = $this->getMultiNotice();
 384+ $wgOut->addHTML( $this->addHeader( '', $oldHeader, $newHeader, $multi ) );
 385+ if( !$allowed ) {
 386+ # Give explanation for why revision is not visible
 387+ $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n",
 388+ array( 'rev-deleted-no-diff' ) );
 389+ } else {
 390+ # Give explanation and add a link to view the diff...
 391+ $link = $this->mTitle->getFullUrl( array(
 392+ 'diff' => $this->mNewid,
 393+ 'oldid' => $this->mOldid,
 394+ 'unhide' => 1
 395+ ) );
 396+ $msg = $suppressed ? 'rev-suppressed-unhide-diff' : 'rev-deleted-unhide-diff';
 397+ $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", array( $msg, $link ) );
 398+ }
 399+ } else if( $wgEnableHtmlDiff && $this->htmldiff ) {
 400+ $multi = $this->getMultiNotice();
 401+ $wgOut->addHTML( '<div class="diff-switchtype">' . $sk->link(
 402+ $this->mTitle,
 403+ wfMsgHtml( 'wikicodecomparison' ),
 404+ array(
 405+ 'id' => 'differences-switchtype'
 406+ ),
 407+ array(
 408+ 'diff' => $this->mNewid,
 409+ 'oldid' => $this->mOldid,
 410+ 'htmldiff' => 0
 411+ ),
 412+ array(
 413+ 'known',
 414+ 'noclasses'
 415+ )
 416+ ) . '</div>');
 417+ $wgOut->addHTML( $this->addHeader( '', $oldHeader, $newHeader, $multi ) );
 418+ $this->renderHtmlDiff();
 419+ } else {
 420+ if( $wgEnableHtmlDiff ) {
 421+ $wgOut->addHTML( '<div class="diff-switchtype">' . $sk->link(
 422+ $this->mTitle,
 423+ wfMsgHtml( 'visualcomparison' ),
 424+ array(
 425+ 'id' => 'differences-switchtype'
 426+ ),
 427+ array(
 428+ 'diff' => $this->mNewid,
 429+ 'oldid' => $this->mOldid,
 430+ 'htmldiff' => 1
 431+ ),
 432+ array(
 433+ 'known',
 434+ 'noclasses'
 435+ )
 436+ ) . '</div>');
 437+ }
 438+ $this->showDiff( $oldHeader, $newHeader );
 439+ if( !$diffOnly ) {
 440+ $this->renderNewRevision();
 441+ }
 442+ }
 443+ wfProfileOut( __METHOD__ );
 444+ }
 445+
 446+ /**
 447+ * Show the new revision of the page.
 448+ */
 449+ function renderNewRevision() {
 450+ global $wgOut, $wgUser;
 451+ wfProfileIn( __METHOD__ );
 452+
 453+ $wgOut->addHTML( "<hr /><h2>{$this->mPagetitle}</h2>\n" );
 454+ # Add deleted rev tag if needed
 455+ if( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
 456+ $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-permission' );
 457+ } else if( $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
 458+ $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-view' );
 459+ }
 460+
 461+ if( !$this->mNewRev->isCurrent() ) {
 462+ $oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false );
 463+ }
 464+
 465+ $this->loadNewText();
 466+ if( is_object( $this->mNewRev ) ) {
 467+ $wgOut->setRevisionId( $this->mNewRev->getId() );
 468+ }
 469+
 470+ if( $this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage() ) {
 471+ // Stolen from Article::view --AG 2007-10-11
 472+ // Give hooks a chance to customise the output
 473+ if( wfRunHooks( 'ShowRawCssJs', array( $this->mNewtext, $this->mTitle, $wgOut ) ) ) {
 474+ // Wrap the whole lot in a <pre> and don't parse
 475+ $m = array();
 476+ preg_match( '!\.(css|js)$!u', $this->mTitle->getText(), $m );
 477+ $wgOut->addHTML( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" );
 478+ $wgOut->addHTML( htmlspecialchars( $this->mNewtext ) );
 479+ $wgOut->addHTML( "\n</pre>\n" );
 480+ }
 481+ } else {
 482+ $wgOut->addWikiTextTidy( $this->mNewtext );
 483+ }
 484+
 485+ if( is_object( $this->mNewRev ) && !$this->mNewRev->isCurrent() ) {
 486+ $wgOut->parserOptions()->setEditSection( $oldEditSectionSetting );
 487+ }
 488+ # Add redundant patrol link on bottom...
 489+ if( $this->mRcidMarkPatrolled && $this->mTitle->quickUserCan('patrol') ) {
 490+ $sk = $wgUser->getSkin();
 491+ $wgOut->addHTML(
 492+ "<div class='patrollink'>[" . $sk->link(
 493+ $this->mTitle,
 494+ wfMsgHtml( 'markaspatrolleddiff' ),
 495+ array(),
 496+ array(
 497+ 'action' => 'markpatrolled',
 498+ 'rcid' => $this->mRcidMarkPatrolled
 499+ )
 500+ ) . ']</div>'
 501+ );
 502+ }
 503+
 504+ wfProfileOut( __METHOD__ );
 505+ }
 506+
 507+
 508+ function renderHtmlDiff() {
 509+ global $wgOut, $wgParser, $wgDebugComments;
 510+ wfProfileIn( __METHOD__ );
 511+
 512+ $this->showDiffStyle();
 513+
 514+ $wgOut->addHTML( '<h2>'.wfMsgHtml( 'visual-comparison' )."</h2>\n" );
 515+ #add deleted rev tag if needed
 516+ if( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
 517+ $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-permission' );
 518+ } else if( $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
 519+ $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-view' );
 520+ }
 521+
 522+ if( !$this->mNewRev->isCurrent() ) {
 523+ $oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false );
 524+ }
 525+
 526+ $this->loadText();
 527+
 528+ // Old revision
 529+ if( is_object( $this->mOldRev ) ) {
 530+ $wgOut->setRevisionId( $this->mOldRev->getId() );
 531+ }
 532+
 533+ $popts = $wgOut->parserOptions();
 534+ $oldTidy = $popts->setTidy( true );
 535+ $popts->setEditSection( false );
 536+
 537+ $parserOutput = $wgParser->parse( $this->mOldtext, $this->getTitle(), $popts, true, true, $wgOut->getRevisionId() );
 538+ $popts->setTidy( $oldTidy );
 539+
 540+ //only for new?
 541+ //$wgOut->addParserOutputNoText( $parserOutput );
 542+ $oldHtml = $parserOutput->getText();
 543+ wfRunHooks( 'OutputPageBeforeHTML', array( &$wgOut, &$oldHtml ) );
 544+
 545+ // New revision
 546+ if( is_object( $this->mNewRev ) ) {
 547+ $wgOut->setRevisionId( $this->mNewRev->getId() );
 548+ }
 549+
 550+ $popts = $wgOut->parserOptions();
 551+ $oldTidy = $popts->setTidy( true );
 552+
 553+ $parserOutput = $wgParser->parse( $this->mNewtext, $this->getTitle(), $popts, true, true, $wgOut->getRevisionId() );
 554+ $popts->setTidy( $oldTidy );
 555+
 556+ $wgOut->addParserOutputNoText( $parserOutput );
 557+ $newHtml = $parserOutput->getText();
 558+ wfRunHooks( 'OutputPageBeforeHTML', array( &$wgOut, &$newHtml ) );
 559+
 560+ unset($parserOutput, $popts);
 561+
 562+ $differ = new HTMLDiffer(new DelegatingContentHandler($wgOut));
 563+ $differ->htmlDiff($oldHtml, $newHtml);
 564+ if ( $wgDebugComments ) {
 565+ $wgOut->addHTML( "\n<!-- HtmlDiff Debug Output:\n" . HTMLDiffer::getDebugOutput() . " End Debug -->" );
 566+ }
 567+
 568+ wfProfileOut( __METHOD__ );
 569+ }
 570+
 571+ /**
 572+ * Show the first revision of an article. Uses normal diff headers in
 573+ * contrast to normal "old revision" display style.
 574+ */
 575+ function showFirstRevision() {
 576+ global $wgOut, $wgUser;
 577+ wfProfileIn( __METHOD__ );
 578+
 579+ # Get article text from the DB
 580+ #
 581+ if ( ! $this->loadNewText() ) {
 582+ $t = $this->mTitle->getPrefixedText();
 583+ $d = wfMsgExt( 'missingarticle-diff', array( 'escape' ), $this->mOldid, $this->mNewid );
 584+ $wgOut->setPagetitle( wfMsg( 'errorpagetitle' ) );
 585+ $wgOut->addWikiMsg( 'missing-article', "<nowiki>$t</nowiki>", $d );
 586+ wfProfileOut( __METHOD__ );
 587+ return;
 588+ }
 589+ if ( $this->mNewRev->isCurrent() ) {
 590+ $wgOut->setArticleFlag( true );
 591+ }
 592+
 593+ # Check if user is allowed to look at this page. If not, bail out.
 594+ #
 595+ if ( !$this->mTitle->userCanRead() ) {
 596+ $wgOut->loginToUse();
 597+ $wgOut->output();
 598+ wfProfileOut( __METHOD__ );
 599+ throw new MWException("Permission Error: you do not have access to view this page");
 600+ }
 601+
 602+ # Prepare the header box
 603+ #
 604+ $sk = $wgUser->getSkin();
 605+
 606+ $next = $this->mTitle->getNextRevisionID( $this->mNewid );
 607+ if( !$next ) {
 608+ $nextlink = '';
 609+ } else {
 610+ $nextlink = '<br/>' . $sk->link(
 611+ $this->mTitle,
 612+ wfMsgHtml( 'nextdiff' ),
 613+ array(
 614+ 'id' => 'differences-nextlink'
 615+ ),
 616+ array(
 617+ 'diff' => 'next',
 618+ 'oldid' => $this->mNewid,
 619+ $this->htmlDiffArgument()
 620+ ),
 621+ array(
 622+ 'known',
 623+ 'noclasses'
 624+ )
 625+ );
 626+ }
 627+ $header = "<div class=\"firstrevisionheader\" style=\"text-align: center\">" .
 628+ $sk->revUserTools( $this->mNewRev ) . "<br/>" . $sk->revComment( $this->mNewRev ) . $nextlink . "</div>\n";
 629+
 630+ $wgOut->addHTML( $header );
 631+
 632+ $wgOut->setSubtitle( wfMsgExt( 'difference', array( 'parseinline' ) ) );
 633+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 634+
 635+ wfProfileOut( __METHOD__ );
 636+ }
 637+
 638+ function htmlDiffArgument(){
 639+ global $wgEnableHtmlDiff;
 640+ if($wgEnableHtmlDiff){
 641+ if($this->htmldiff){
 642+ return array( 'htmldiff' => 1 );
 643+ }else{
 644+ return array( 'htmldiff' => 0 );
 645+ }
 646+ }else{
 647+ return array();
 648+ }
 649+ }
 650+
 651+ /**
 652+ * Get the diff text, send it to $wgOut
 653+ * Returns false if the diff could not be generated, otherwise returns true
 654+ */
 655+ function showDiff( $otitle, $ntitle ) {
 656+ global $wgOut;
 657+ $diff = $this->getDiff( $otitle, $ntitle );
 658+ if ( $diff === false ) {
 659+ $wgOut->addWikiMsg( 'missing-article', "<nowiki>(fixme, bug)</nowiki>", '' );
 660+ return false;
 661+ } else {
 662+ $this->showDiffStyle();
 663+ $wgOut->addHTML( $diff );
 664+ return true;
 665+ }
 666+ }
 667+
 668+ /**
 669+ * Add style sheets and supporting JS for diff display.
 670+ */
 671+ function showDiffStyle() {
 672+ global $wgStylePath, $wgStyleVersion, $wgOut;
 673+ $wgOut->addStyle( 'common/diff.css' );
 674+
 675+ // JS is needed to detect old versions of Mozilla to work around an annoyance bug.
 676+ $wgOut->addScript( "<script type=\"text/javascript\" src=\"$wgStylePath/common/diff.js?$wgStyleVersion\"></script>" );
 677+ }
 678+
 679+ /**
 680+ * Get complete diff table, including header
 681+ *
 682+ * @param Title $otitle Old title
 683+ * @param Title $ntitle New title
 684+ * @return mixed
 685+ */
 686+ function getDiff( $otitle, $ntitle ) {
 687+ $body = $this->getDiffBody();
 688+ if ( $body === false ) {
 689+ return false;
 690+ } else {
 691+ $multi = $this->getMultiNotice();
 692+ return $this->addHeader( $body, $otitle, $ntitle, $multi );
 693+ }
 694+ }
 695+
 696+ /**
 697+ * Get the diff table body, without header
 698+ *
 699+ * @return mixed
 700+ */
 701+ function getDiffBody() {
 702+ global $wgMemc;
 703+ wfProfileIn( __METHOD__ );
 704+ $this->mCacheHit = true;
 705+ // Check if the diff should be hidden from this user
 706+ if ( !$this->loadRevisionData() )
 707+ return '';
 708+ if ( $this->mOldRev && !$this->mOldRev->userCan(Revision::DELETED_TEXT) ) {
 709+ return '';
 710+ } else if ( $this->mNewRev && !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
 711+ return '';
 712+ } else if ( $this->mOldRev && $this->mNewRev && $this->mOldRev->getID() == $this->mNewRev->getID() ) {
 713+ return '';
 714+ }
 715+ // Cacheable?
 716+ $key = false;
 717+ if ( $this->mOldid && $this->mNewid ) {
 718+ $key = wfMemcKey( 'diff', 'version', MW_DIFF_VERSION, 'oldid', $this->mOldid, 'newid', $this->mNewid );
 719+ // Try cache
 720+ if ( !$this->mRefreshCache ) {
 721+ $difftext = $wgMemc->get( $key );
 722+ if ( $difftext ) {
 723+ wfIncrStats( 'diff_cache_hit' );
 724+ $difftext = $this->localiseLineNumbers( $difftext );
 725+ $difftext .= "\n<!-- diff cache key $key -->\n";
 726+ wfProfileOut( __METHOD__ );
 727+ return $difftext;
 728+ }
 729+ } // don't try to load but save the result
 730+ }
 731+ $this->mCacheHit = false;
 732+
 733+ // Loadtext is permission safe, this just clears out the diff
 734+ if ( !$this->loadText() ) {
 735+ wfProfileOut( __METHOD__ );
 736+ return false;
 737+ }
 738+
 739+ $difftext = $this->generateDiffBody( $this->mOldtext, $this->mNewtext );
 740+
 741+ // Save to cache for 7 days
 742+ if ( !wfRunHooks( 'AbortDiffCache', array( &$this ) ) ) {
 743+ wfIncrStats( 'diff_uncacheable' );
 744+ } else if ( $key !== false && $difftext !== false ) {
 745+ wfIncrStats( 'diff_cache_miss' );
 746+ $wgMemc->set( $key, $difftext, 7*86400 );
 747+ } else {
 748+ wfIncrStats( 'diff_uncacheable' );
 749+ }
 750+ // Replace line numbers with the text in the user's language
 751+ if ( $difftext !== false ) {
 752+ $difftext = $this->localiseLineNumbers( $difftext );
 753+ }
 754+ wfProfileOut( __METHOD__ );
 755+ return $difftext;
 756+ }
 757+
 758+ /**
 759+ * Make sure the proper modules are loaded before we try to
 760+ * make the diff
 761+ */
 762+ private function initDiffEngines() {
 763+ global $wgExternalDiffEngine;
 764+ if ( $wgExternalDiffEngine == 'wikidiff' && !function_exists( 'wikidiff_do_diff' ) ) {
 765+ wfProfileIn( __METHOD__ . '-php_wikidiff.so' );
 766+ wfSuppressWarnings();
 767+ dl( 'php_wikidiff.so' );
 768+ wfRestoreWarnings();
 769+ wfProfileOut( __METHOD__ . '-php_wikidiff.so' );
 770+ }
 771+ else if ( $wgExternalDiffEngine == 'wikidiff2' && !function_exists( 'wikidiff2_do_diff' ) ) {
 772+ wfProfileIn( __METHOD__ . '-php_wikidiff2.so' );
 773+ wfSuppressWarnings();
 774+ dl( 'php_wikidiff2.so' );
 775+ wfRestoreWarnings();
 776+ wfProfileOut( __METHOD__ . '-php_wikidiff2.so' );
 777+ }
 778+ }
 779+
 780+ /**
 781+ * Generate a diff, no caching
 782+ * $otext and $ntext must be already segmented
 783+ */
 784+ function generateDiffBody( $otext, $ntext ) {
 785+ global $wgExternalDiffEngine, $wgContLang;
 786+
 787+ $otext = str_replace( "\r\n", "\n", $otext );
 788+ $ntext = str_replace( "\r\n", "\n", $ntext );
 789+
 790+ $this->initDiffEngines();
 791+
 792+ if ( $wgExternalDiffEngine == 'wikidiff' && function_exists( 'wikidiff_do_diff' ) ) {
 793+ # For historical reasons, external diff engine expects
 794+ # input text to be HTML-escaped already
 795+ $otext = htmlspecialchars ( $wgContLang->segmentForDiff( $otext ) );
 796+ $ntext = htmlspecialchars ( $wgContLang->segmentForDiff( $ntext ) );
 797+ return $wgContLang->unsegementForDiff( wikidiff_do_diff( $otext, $ntext, 2 ) ) .
 798+ $this->debug( 'wikidiff1' );
 799+ }
 800+
 801+ if ( $wgExternalDiffEngine == 'wikidiff2' && function_exists( 'wikidiff2_do_diff' ) ) {
 802+ # Better external diff engine, the 2 may some day be dropped
 803+ # This one does the escaping and segmenting itself
 804+ wfProfileIn( 'wikidiff2_do_diff' );
 805+ $text = wikidiff2_do_diff( $otext, $ntext, 2 );
 806+ $text .= $this->debug( 'wikidiff2' );
 807+ wfProfileOut( 'wikidiff2_do_diff' );
 808+ return $text;
 809+ }
 810+ if ( $wgExternalDiffEngine != 'wikidiff3' && $wgExternalDiffEngine !== false ) {
 811+ # Diff via the shell
 812+ global $wgTmpDirectory;
 813+ $tempName1 = tempnam( $wgTmpDirectory, 'diff_' );
 814+ $tempName2 = tempnam( $wgTmpDirectory, 'diff_' );
 815+
 816+ $tempFile1 = fopen( $tempName1, "w" );
 817+ if ( !$tempFile1 ) {
 818+ wfProfileOut( __METHOD__ );
 819+ return false;
 820+ }
 821+ $tempFile2 = fopen( $tempName2, "w" );
 822+ if ( !$tempFile2 ) {
 823+ wfProfileOut( __METHOD__ );
 824+ return false;
 825+ }
 826+ fwrite( $tempFile1, $otext );
 827+ fwrite( $tempFile2, $ntext );
 828+ fclose( $tempFile1 );
 829+ fclose( $tempFile2 );
 830+ $cmd = wfEscapeShellArg( $wgExternalDiffEngine, $tempName1, $tempName2 );
 831+ wfProfileIn( __METHOD__ . "-shellexec" );
 832+ $difftext = wfShellExec( $cmd );
 833+ $difftext .= $this->debug( "external $wgExternalDiffEngine" );
 834+ wfProfileOut( __METHOD__ . "-shellexec" );
 835+ unlink( $tempName1 );
 836+ unlink( $tempName2 );
 837+ return $difftext;
 838+ }
 839+
 840+ # Native PHP diff
 841+ $ota = explode( "\n", $wgContLang->segmentForDiff( $otext ) );
 842+ $nta = explode( "\n", $wgContLang->segmentForDiff( $ntext ) );
 843+ $diffs = new Diff( $ota, $nta );
 844+ $formatter = new TableDiffFormatter();
 845+ return $wgContLang->unsegmentForDiff( $formatter->format( $diffs ) ) .
 846+ $this->debug();
 847+ }
 848+
 849+ /**
 850+ * Generate a debug comment indicating diff generating time,
 851+ * server node, and generator backend.
 852+ */
 853+ protected function debug( $generator="internal" ) {
 854+ global $wgShowHostnames;
 855+ if ( !$this->enableDebugComment ) {
 856+ return '';
 857+ }
 858+ $data = array( $generator );
 859+ if( $wgShowHostnames ) {
 860+ $data[] = wfHostname();
 861+ }
 862+ $data[] = wfTimestamp( TS_DB );
 863+ return "<!-- diff generator: " .
 864+ implode( " ",
 865+ array_map(
 866+ "htmlspecialchars",
 867+ $data ) ) .
 868+ " -->\n";
 869+ }
 870+
 871+ /**
 872+ * Replace line numbers with the text in the user's language
 873+ */
 874+ function localiseLineNumbers( $text ) {
 875+ return preg_replace_callback( '/<!--LINE (\d+)-->/',
 876+ array( &$this, 'localiseLineNumbersCb' ), $text );
 877+ }
 878+
 879+ function localiseLineNumbersCb( $matches ) {
 880+ global $wgLang;
 881+ if ( $matches[1] === '1' && $this->mReducedLineNumbers ) return '';
 882+ return wfMsgExt( 'lineno', 'escape', $wgLang->formatNum( $matches[1] ) );
 883+ }
 884+
 885+
 886+ /**
 887+ * If there are revisions between the ones being compared, return a note saying so.
 888+ */
 889+ function getMultiNotice() {
 890+ if ( !is_object($this->mOldRev) || !is_object($this->mNewRev) )
 891+ return '';
 892+
 893+ if( !$this->mOldPage->equals( $this->mNewPage ) ) {
 894+ // Comparing two different pages? Count would be meaningless.
 895+ return '';
 896+ }
 897+
 898+ $oldid = $this->mOldRev->getId();
 899+ $newid = $this->mNewRev->getId();
 900+ if ( $oldid > $newid ) {
 901+ $tmp = $oldid; $oldid = $newid; $newid = $tmp;
 902+ }
 903+
 904+ $n = $this->mTitle->countRevisionsBetween( $oldid, $newid );
 905+ if ( !$n )
 906+ return '';
 907+
 908+ return wfMsgExt( 'diff-multi', array( 'parseinline' ), $n );
 909+ }
 910+
 911+
 912+ /**
 913+ * Add the header to a diff body
 914+ */
 915+ static function addHeader( $diff, $otitle, $ntitle, $multi = '' ) {
 916+ $colspan = 1;
 917+ $header = "<table class='diff'>";
 918+ if( $diff ) { // Safari/Chrome show broken output if cols not used
 919+ $header .= "
 920+ <col class='diff-marker' />
 921+ <col class='diff-content' />
 922+ <col class='diff-marker' />
 923+ <col class='diff-content' />";
 924+ $colspan = 2;
 925+ }
 926+ $header .= "
 927+ <tr valign='top'>
 928+ <td colspan='$colspan' class='diff-otitle'>{$otitle}</td>
 929+ <td colspan='$colspan' class='diff-ntitle'>{$ntitle}</td>
 930+ </tr>";
 931+
 932+ if ( $multi != '' )
 933+ $header .= "<tr><td colspan='4' align='center' class='diff-multi'>{$multi}</td></tr>";
 934+
 935+ return $header . $diff . "</table>";
 936+ }
 937+
 938+ /**
 939+ * Use specified text instead of loading from the database
 940+ */
 941+ function setText( $oldText, $newText ) {
 942+ $this->mOldtext = $oldText;
 943+ $this->mNewtext = $newText;
 944+ $this->mTextLoaded = 2;
 945+ $this->mRevisionsLoaded = true;
 946+ }
 947+
 948+ /**
 949+ * Load revision metadata for the specified articles. If newid is 0, then compare
 950+ * the old article in oldid to the current article; if oldid is 0, then
 951+ * compare the current article to the immediately previous one (ignoring the
 952+ * value of newid).
 953+ *
 954+ * If oldid is false, leave the corresponding revision object set
 955+ * to false. This is impossible via ordinary user input, and is provided for
 956+ * API convenience.
 957+ */
 958+ function loadRevisionData() {
 959+ global $wgLang, $wgUser;
 960+ if ( $this->mRevisionsLoaded ) {
 961+ return true;
 962+ } else {
 963+ // Whether it succeeds or fails, we don't want to try again
 964+ $this->mRevisionsLoaded = true;
 965+ }
 966+
 967+ // Load the new revision object
 968+ $this->mNewRev = $this->mNewid
 969+ ? Revision::newFromId( $this->mNewid )
 970+ : Revision::newFromTitle( $this->mTitle );
 971+ if( !$this->mNewRev instanceof Revision )
 972+ return false;
 973+
 974+ // Update the new revision ID in case it was 0 (makes life easier doing UI stuff)
 975+ $this->mNewid = $this->mNewRev->getId();
 976+
 977+ // Check if page is editable
 978+ $editable = $this->mNewRev->getTitle()->userCan( 'edit' );
 979+
 980+ // Set assorted variables
 981+ $timestamp = $wgLang->timeanddate( $this->mNewRev->getTimestamp(), true );
 982+ $dateofrev = $wgLang->date( $this->mNewRev->getTimestamp(), true );
 983+ $timeofrev = $wgLang->time( $this->mNewRev->getTimestamp(), true );
 984+ $this->mNewPage = $this->mNewRev->getTitle();
 985+ if( $this->mNewRev->isCurrent() ) {
 986+ $newLink = $this->mNewPage->escapeLocalUrl( array(
 987+ 'oldid' => $this->mNewid
 988+ ) );
 989+ $this->mPagetitle = htmlspecialchars( wfMsg(
 990+ 'currentrev-asof',
 991+ $timestamp,
 992+ $dateofrev,
 993+ $timeofrev
 994+ ) );
 995+ $newEdit = $this->mNewPage->escapeLocalUrl( array(
 996+ 'action' => 'edit'
 997+ ) );
 998+
 999+ $this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a>";
 1000+ $this->mNewtitle .= " (<a href='$newEdit'>" . wfMsgHtml( $editable ? 'editold' : 'viewsourceold' ) . "</a>)";
 1001+ } else {
 1002+ $newLink = $this->mNewPage->escapeLocalUrl( array(
 1003+ 'oldid' => $this->mNewid
 1004+ ) );
 1005+ $newEdit = $this->mNewPage->escapeLocalUrl( array(
 1006+ 'action' => 'edit',
 1007+ 'oldid' => $this->mNewid
 1008+ ) );
 1009+ $this->mPagetitle = htmlspecialchars( wfMsg(
 1010+ 'revisionasof',
 1011+ $timestamp,
 1012+ $dateofrev,
 1013+ $timeofrev
 1014+ ) );
 1015+
 1016+ $this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a>";
 1017+ $this->mNewtitle .= " (<a href='$newEdit'>" . wfMsgHtml( $editable ? 'editold' : 'viewsourceold' ) . "</a>)";
 1018+ }
 1019+ if( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
 1020+ $this->mNewtitle = "<span class='history-deleted'>{$this->mPagetitle}</span>";
 1021+ } else if ( $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
 1022+ $this->mNewtitle = "<span class='history-deleted'>{$this->mNewtitle}</span>";
 1023+ }
 1024+
 1025+ // Load the old revision object
 1026+ $this->mOldRev = false;
 1027+ if( $this->mOldid ) {
 1028+ $this->mOldRev = Revision::newFromId( $this->mOldid );
 1029+ } elseif ( $this->mOldid === 0 ) {
 1030+ $rev = $this->mNewRev->getPrevious();
 1031+ if( $rev ) {
 1032+ $this->mOldid = $rev->getId();
 1033+ $this->mOldRev = $rev;
 1034+ } else {
 1035+ // No previous revision; mark to show as first-version only.
 1036+ $this->mOldid = false;
 1037+ $this->mOldRev = false;
 1038+ }
 1039+ }/* elseif ( $this->mOldid === false ) leave mOldRev false; */
 1040+
 1041+ if( is_null( $this->mOldRev ) ) {
 1042+ return false;
 1043+ }
 1044+
 1045+ if ( $this->mOldRev ) {
 1046+ $this->mOldPage = $this->mOldRev->getTitle();
 1047+
 1048+ $t = $wgLang->timeanddate( $this->mOldRev->getTimestamp(), true );
 1049+ $dateofrev = $wgLang->date( $this->mOldRev->getTimestamp(), true );
 1050+ $timeofrev = $wgLang->time( $this->mOldRev->getTimestamp(), true );
 1051+ $oldLink = $this->mOldPage->escapeLocalUrl( array(
 1052+ 'oldid' => $this->mOldid
 1053+ ) );
 1054+ $oldEdit = $this->mOldPage->escapeLocalUrl( array(
 1055+ 'action' => 'edit',
 1056+ 'oldid' => $this->mOldid
 1057+ ) );
 1058+ $this->mOldPagetitle = htmlspecialchars( wfMsg( 'revisionasof', $t, $dateofrev, $timeofrev ) );
 1059+
 1060+ $this->mOldtitle = "<a href='$oldLink'>{$this->mOldPagetitle}</a>"
 1061+ . " (<a href='$oldEdit'>" . wfMsgHtml( $editable ? 'editold' : 'viewsourceold' ) . "</a>)";
 1062+ // Add an "undo" link
 1063+ $newUndo = $this->mNewPage->escapeLocalUrl( array(
 1064+ 'action' => 'edit',
 1065+ 'undoafter' => $this->mOldid,
 1066+ 'undo' => $this->mNewid
 1067+ ) );
 1068+ $htmlLink = htmlspecialchars( wfMsg( 'editundo' ) );
 1069+ $htmlTitle = $wgUser->getSkin()->tooltip( 'undo' );
 1070+ if( $editable && !$this->mOldRev->isDeleted( Revision::DELETED_TEXT ) && !$this->mNewRev->isDeleted( Revision::DELETED_TEXT ) ) {
 1071+ $this->mNewtitle .= " (<a href='$newUndo' $htmlTitle>" . $htmlLink . "</a>)";
 1072+ }
 1073+
 1074+ if( !$this->mOldRev->userCan( Revision::DELETED_TEXT ) ) {
 1075+ $this->mOldtitle = '<span class="history-deleted">' . $this->mOldPagetitle . '</span>';
 1076+ } else if( $this->mOldRev->isDeleted( Revision::DELETED_TEXT ) ) {
 1077+ $this->mOldtitle = '<span class="history-deleted">' . $this->mOldtitle . '</span>';
 1078+ }
 1079+ }
 1080+
 1081+ return true;
 1082+ }
 1083+
 1084+ /**
 1085+ * Load the text of the revisions, as well as revision data.
 1086+ */
 1087+ function loadText() {
 1088+ if ( $this->mTextLoaded == 2 ) {
 1089+ return true;
 1090+ } else {
 1091+ // Whether it succeeds or fails, we don't want to try again
 1092+ $this->mTextLoaded = 2;
 1093+ }
 1094+
 1095+ if ( !$this->loadRevisionData() ) {
 1096+ return false;
 1097+ }
 1098+ if ( $this->mOldRev ) {
 1099+ $this->mOldtext = $this->mOldRev->getText( Revision::FOR_THIS_USER );
 1100+ if ( $this->mOldtext === false ) {
 1101+ return false;
 1102+ }
 1103+ }
 1104+ if ( $this->mNewRev ) {
 1105+ $this->mNewtext = $this->mNewRev->getText( Revision::FOR_THIS_USER );
 1106+ if ( $this->mNewtext === false ) {
 1107+ return false;
 1108+ }
 1109+ }
 1110+ return true;
 1111+ }
 1112+
 1113+ /**
 1114+ * Load the text of the new revision, not the old one
 1115+ */
 1116+ function loadNewText() {
 1117+ if ( $this->mTextLoaded >= 1 ) {
 1118+ return true;
 1119+ } else {
 1120+ $this->mTextLoaded = 1;
 1121+ }
 1122+ if ( !$this->loadRevisionData() ) {
 1123+ return false;
 1124+ }
 1125+ $this->mNewtext = $this->mNewRev->getText( Revision::FOR_THIS_USER );
 1126+ return true;
 1127+ }
 1128+}
Property changes on: trunk/phase3/includes/diff/DifferenceInterface.php
___________________________________________________________________
Name: svn:eol-style
11291129 + native

Status & tagging log