MediaWiki
REL1_22
|
00001 <?php 00036 class HistoryAction extends FormlessAction { 00037 const DIR_PREV = 0; 00038 const DIR_NEXT = 1; 00039 00040 public function getName() { 00041 return 'history'; 00042 } 00043 00044 public function requiresWrite() { 00045 return false; 00046 } 00047 00048 public function requiresUnblock() { 00049 return false; 00050 } 00051 00052 protected function getPageTitle() { 00053 return $this->msg( 'history-title', $this->getTitle()->getPrefixedText() )->text(); 00054 } 00055 00056 protected function getDescription() { 00057 // Creation of a subtitle link pointing to [[Special:Log]] 00058 return Linker::linkKnown( 00059 SpecialPage::getTitleFor( 'Log' ), 00060 $this->msg( 'viewpagelogs' )->escaped(), 00061 array(), 00062 array( 'page' => $this->getTitle()->getPrefixedText() ) 00063 ); 00064 } 00065 00070 public function getArticle() { 00071 return $this->page; 00072 } 00073 00078 private function preCacheMessages() { 00079 // Precache various messages 00080 if ( !isset( $this->message ) ) { 00081 $msgs = array( 'cur', 'last', 'pipe-separator' ); 00082 foreach ( $msgs as $msg ) { 00083 $this->message[$msg] = $this->msg( $msg )->escaped(); 00084 } 00085 } 00086 } 00087 00091 function onView() { 00092 global $wgScript, $wgUseFileCache; 00093 00094 $out = $this->getOutput(); 00095 $request = $this->getRequest(); 00096 00100 if ( $out->checkLastModified( $this->page->getTouched() ) ) { 00101 return; // Client cache fresh and headers sent, nothing more to do. 00102 } 00103 00104 wfProfileIn( __METHOD__ ); 00105 00106 $this->preCacheMessages(); 00107 00108 # Fill in the file cache if not set already 00109 if ( $wgUseFileCache && HTMLFileCache::useFileCache( $this->getContext() ) ) { 00110 $cache = HTMLFileCache::newFromTitle( $this->getTitle(), 'history' ); 00111 if ( !$cache->isCacheGood( /* Assume up to date */ ) ) { 00112 ob_start( array( &$cache, 'saveToFileCache' ) ); 00113 } 00114 } 00115 00116 // Setup page variables. 00117 $out->setFeedAppendQuery( 'action=history' ); 00118 $out->addModules( 'mediawiki.action.history' ); 00119 00120 // Handle atom/RSS feeds. 00121 $feedType = $request->getVal( 'feed' ); 00122 if ( $feedType ) { 00123 $this->feed( $feedType ); 00124 wfProfileOut( __METHOD__ ); 00125 return; 00126 } 00127 00128 // Fail nicely if article doesn't exist. 00129 if ( !$this->page->exists() ) { 00130 $out->addWikiMsg( 'nohistory' ); 00131 # show deletion/move log if there is an entry 00132 LogEventsList::showLogExtract( 00133 $out, 00134 array( 'delete', 'move' ), 00135 $this->getTitle(), 00136 '', 00137 array( 'lim' => 10, 00138 'conds' => array( "log_action != 'revision'" ), 00139 'showIfEmpty' => false, 00140 'msgKey' => array( 'moveddeleted-notice' ) 00141 ) 00142 ); 00143 wfProfileOut( __METHOD__ ); 00144 return; 00145 } 00146 00150 $year = $request->getInt( 'year' ); 00151 $month = $request->getInt( 'month' ); 00152 $tagFilter = $request->getVal( 'tagfilter' ); 00153 $tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter ); 00154 00158 if ( $request->getBool( 'deleted' ) ) { 00159 $conds = array( 'rev_deleted != 0' ); 00160 } else { 00161 $conds = array(); 00162 } 00163 if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) { 00164 $checkDeleted = Xml::checkLabel( $this->msg( 'history-show-deleted' )->text(), 00165 'deleted', 'mw-show-deleted-only', $request->getBool( 'deleted' ) ) . "\n"; 00166 } else { 00167 $checkDeleted = ''; 00168 } 00169 00170 // Add the general form 00171 $action = htmlspecialchars( $wgScript ); 00172 $out->addHTML( 00173 "<form action=\"$action\" method=\"get\" id=\"mw-history-searchform\">" . 00174 Xml::fieldset( 00175 $this->msg( 'history-fieldset-title' )->text(), 00176 false, 00177 array( 'id' => 'mw-history-search' ) 00178 ) . 00179 Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . "\n" . 00180 Html::hidden( 'action', 'history' ) . "\n" . 00181 Xml::dateMenu( ( $year == null ? MWTimestamp::getLocalInstance()->format( 'Y' ) : $year ), $month ) . ' ' . 00182 ( $tagSelector ? ( implode( ' ', $tagSelector ) . ' ' ) : '' ) . 00183 $checkDeleted . 00184 Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ) . "\n" . 00185 '</fieldset></form>' 00186 ); 00187 00188 wfRunHooks( 'PageHistoryBeforeList', array( &$this->page, $this->getContext() ) ); 00189 00190 // Create and output the list. 00191 $pager = new HistoryPager( $this, $year, $month, $tagFilter, $conds ); 00192 $out->addHTML( 00193 $pager->getNavigationBar() . 00194 $pager->getBody() . 00195 $pager->getNavigationBar() 00196 ); 00197 $out->preventClickjacking( $pager->getPreventClickjacking() ); 00198 00199 wfProfileOut( __METHOD__ ); 00200 } 00201 00212 function fetchRevisions( $limit, $offset, $direction ) { 00213 // Fail if article doesn't exist. 00214 if ( !$this->getTitle()->exists() ) { 00215 return new FakeResultWrapper( array() ); 00216 } 00217 00218 $dbr = wfGetDB( DB_SLAVE ); 00219 00220 if ( $direction == HistoryPage::DIR_PREV ) { 00221 list( $dirs, $oper ) = array( "ASC", ">=" ); 00222 } else { /* $direction == HistoryPage::DIR_NEXT */ 00223 list( $dirs, $oper ) = array( "DESC", "<=" ); 00224 } 00225 00226 if ( $offset ) { 00227 $offsets = array( "rev_timestamp $oper " . $dbr->addQuotes( $dbr->timestamp( $offset ) ) ); 00228 } else { 00229 $offsets = array(); 00230 } 00231 00232 $page_id = $this->page->getId(); 00233 00234 return $dbr->select( 'revision', 00235 Revision::selectFields(), 00236 array_merge( array( 'rev_page' => $page_id ), $offsets ), 00237 __METHOD__, 00238 array( 'ORDER BY' => "rev_timestamp $dirs", 00239 'USE INDEX' => 'page_timestamp', 'LIMIT' => $limit ) 00240 ); 00241 } 00242 00248 function feed( $type ) { 00249 global $wgFeedClasses, $wgFeedLimit; 00250 if ( !FeedUtils::checkFeedOutput( $type ) ) { 00251 return; 00252 } 00253 $request = $this->getRequest(); 00254 00255 $feed = new $wgFeedClasses[$type]( 00256 $this->getTitle()->getPrefixedText() . ' - ' . 00257 $this->msg( 'history-feed-title' )->inContentLanguage()->text(), 00258 $this->msg( 'history-feed-description' )->inContentLanguage()->text(), 00259 $this->getTitle()->getFullURL( 'action=history' ) 00260 ); 00261 00262 // Get a limit on number of feed entries. Provide a sane default 00263 // of 10 if none is defined (but limit to $wgFeedLimit max) 00264 $limit = $request->getInt( 'limit', 10 ); 00265 $limit = min( max( $limit, 1 ), $wgFeedLimit ); 00266 00267 $items = $this->fetchRevisions( $limit, 0, HistoryPage::DIR_NEXT ); 00268 00269 // Generate feed elements enclosed between header and footer. 00270 $feed->outHeader(); 00271 if ( $items->numRows() ) { 00272 foreach ( $items as $row ) { 00273 $feed->outItem( $this->feedItem( $row ) ); 00274 } 00275 } else { 00276 $feed->outItem( $this->feedEmpty() ); 00277 } 00278 $feed->outFooter(); 00279 } 00280 00281 function feedEmpty() { 00282 return new FeedItem( 00283 $this->msg( 'nohistory' )->inContentLanguage()->text(), 00284 $this->msg( 'history-feed-empty' )->inContentLanguage()->parseAsBlock(), 00285 $this->getTitle()->getFullURL(), 00286 wfTimestamp( TS_MW ), 00287 '', 00288 $this->getTitle()->getTalkPage()->getFullURL() 00289 ); 00290 } 00291 00300 function feedItem( $row ) { 00301 $rev = new Revision( $row ); 00302 $rev->setTitle( $this->getTitle() ); 00303 $text = FeedUtils::formatDiffRow( 00304 $this->getTitle(), 00305 $this->getTitle()->getPreviousRevisionID( $rev->getId() ), 00306 $rev->getId(), 00307 $rev->getTimestamp(), 00308 $rev->getComment() 00309 ); 00310 if ( $rev->getComment() == '' ) { 00311 global $wgContLang; 00312 $title = $this->msg( 'history-feed-item-nocomment', 00313 $rev->getUserText(), 00314 $wgContLang->timeanddate( $rev->getTimestamp() ), 00315 $wgContLang->date( $rev->getTimestamp() ), 00316 $wgContLang->time( $rev->getTimestamp() ) )->inContentLanguage()->text(); 00317 } else { 00318 $title = $rev->getUserText() . 00319 $this->msg( 'colon-separator' )->inContentLanguage()->text() . 00320 FeedItem::stripComment( $rev->getComment() ); 00321 } 00322 return new FeedItem( 00323 $title, 00324 $text, 00325 $this->getTitle()->getFullURL( 'diff=' . $rev->getId() . '&oldid=prev' ), 00326 $rev->getTimestamp(), 00327 $rev->getUserText(), 00328 $this->getTitle()->getTalkPage()->getFullURL() 00329 ); 00330 } 00331 } 00332 00337 class HistoryPager extends ReverseChronologicalPager { 00338 public $lastRow = false, $counter, $historyPage, $buttons, $conds; 00339 protected $oldIdChecked; 00340 protected $preventClickjacking = false; 00344 protected $parentLens; 00345 00346 function __construct( $historyPage, $year = '', $month = '', $tagFilter = '', $conds = array() ) { 00347 parent::__construct( $historyPage->getContext() ); 00348 $this->historyPage = $historyPage; 00349 $this->tagFilter = $tagFilter; 00350 $this->getDateCond( $year, $month ); 00351 $this->conds = $conds; 00352 } 00353 00354 // For hook compatibility... 00355 function getArticle() { 00356 return $this->historyPage->getArticle(); 00357 } 00358 00359 function getSqlComment() { 00360 if ( $this->conds ) { 00361 return 'history page filtered'; // potentially slow, see CR r58153 00362 } else { 00363 return 'history page unfiltered'; 00364 } 00365 } 00366 00367 function getQueryInfo() { 00368 $queryInfo = array( 00369 'tables' => array( 'revision', 'user' ), 00370 'fields' => array_merge( Revision::selectFields(), Revision::selectUserFields() ), 00371 'conds' => array_merge( 00372 array( 'rev_page' => $this->getWikiPage()->getId() ), 00373 $this->conds ), 00374 'options' => array( 'USE INDEX' => array( 'revision' => 'page_timestamp' ) ), 00375 'join_conds' => array( 'user' => Revision::userJoinCond() ), 00376 ); 00377 ChangeTags::modifyDisplayQuery( 00378 $queryInfo['tables'], 00379 $queryInfo['fields'], 00380 $queryInfo['conds'], 00381 $queryInfo['join_conds'], 00382 $queryInfo['options'], 00383 $this->tagFilter 00384 ); 00385 wfRunHooks( 'PageHistoryPager::getQueryInfo', array( &$this, &$queryInfo ) ); 00386 return $queryInfo; 00387 } 00388 00389 function getIndexField() { 00390 return 'rev_timestamp'; 00391 } 00392 00393 function formatRow( $row ) { 00394 if ( $this->lastRow ) { 00395 $latest = ( $this->counter == 1 && $this->mIsFirst ); 00396 $firstInList = $this->counter == 1; 00397 $this->counter++; 00398 $s = $this->historyLine( $this->lastRow, $row, 00399 $this->getTitle()->getNotificationTimestamp( $this->getUser() ), $latest, $firstInList ); 00400 } else { 00401 $s = ''; 00402 } 00403 $this->lastRow = $row; 00404 return $s; 00405 } 00406 00407 function doBatchLookups() { 00408 # Do a link batch query 00409 $this->mResult->seek( 0 ); 00410 $batch = new LinkBatch(); 00411 $revIds = array(); 00412 foreach ( $this->mResult as $row ) { 00413 if ( $row->rev_parent_id ) { 00414 $revIds[] = $row->rev_parent_id; 00415 } 00416 if ( !is_null( $row->user_name ) ) { 00417 $batch->add( NS_USER, $row->user_name ); 00418 $batch->add( NS_USER_TALK, $row->user_name ); 00419 } else { # for anons or usernames of imported revisions 00420 $batch->add( NS_USER, $row->rev_user_text ); 00421 $batch->add( NS_USER_TALK, $row->rev_user_text ); 00422 } 00423 } 00424 $this->parentLens = Revision::getParentLengths( $this->mDb, $revIds ); 00425 $batch->execute(); 00426 $this->mResult->seek( 0 ); 00427 } 00428 00434 function getStartBody() { 00435 global $wgScript; 00436 $this->lastRow = false; 00437 $this->counter = 1; 00438 $this->oldIdChecked = 0; 00439 00440 $this->getOutput()->wrapWikiMsg( "<div class='mw-history-legend'>\n$1\n</div>", 'histlegend' ); 00441 $s = Html::openElement( 'form', array( 'action' => $wgScript, 00442 'id' => 'mw-history-compare' ) ) . "\n"; 00443 $s .= Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . "\n"; 00444 $s .= Html::hidden( 'action', 'historysubmit' ) . "\n"; 00445 00446 // Button container stored in $this->buttons for re-use in getEndBody() 00447 $this->buttons = '<div>'; 00448 $this->buttons .= $this->submitButton( $this->msg( 'compareselectedversions' )->text(), 00449 array( 'class' => 'historysubmit mw-history-compareselectedversions-button' ) 00450 + Linker::tooltipAndAccesskeyAttribs( 'compareselectedversions' ) 00451 ) . "\n"; 00452 00453 if ( $this->getUser()->isAllowed( 'deleterevision' ) ) { 00454 $this->buttons .= $this->getRevisionButton( 'revisiondelete', 'showhideselectedversions' ); 00455 } 00456 $this->buttons .= '</div>'; 00457 00458 $s .= $this->buttons; 00459 $s .= '<ul id="pagehistory">' . "\n"; 00460 return $s; 00461 } 00462 00463 private function getRevisionButton( $name, $msg ) { 00464 $this->preventClickjacking(); 00465 # Note bug #20966, <button> is non-standard in IE<8 00466 $element = Html::element( 'button', 00467 array( 00468 'type' => 'submit', 00469 'name' => $name, 00470 'value' => '1', 00471 'class' => "historysubmit mw-history-$name-button", 00472 ), 00473 $this->msg( $msg )->text() 00474 ) . "\n"; 00475 return $element; 00476 } 00477 00478 function getEndBody() { 00479 if ( $this->lastRow ) { 00480 $latest = $this->counter == 1 && $this->mIsFirst; 00481 $firstInList = $this->counter == 1; 00482 if ( $this->mIsBackwards ) { 00483 # Next row is unknown, but for UI reasons, probably exists if an offset has been specified 00484 if ( $this->mOffset == '' ) { 00485 $next = null; 00486 } else { 00487 $next = 'unknown'; 00488 } 00489 } else { 00490 # The next row is the past-the-end row 00491 $next = $this->mPastTheEndRow; 00492 } 00493 $this->counter++; 00494 $s = $this->historyLine( $this->lastRow, $next, 00495 $this->getTitle()->getNotificationTimestamp( $this->getUser() ), $latest, $firstInList ); 00496 } else { 00497 $s = ''; 00498 } 00499 $s .= "</ul>\n"; 00500 # Add second buttons only if there is more than one rev 00501 if ( $this->getNumRows() > 2 ) { 00502 $s .= $this->buttons; 00503 } 00504 $s .= '</form>'; 00505 return $s; 00506 } 00507 00515 function submitButton( $message, $attributes = array() ) { 00516 # Disable submit button if history has 1 revision only 00517 if ( $this->getNumRows() > 1 ) { 00518 return Xml::submitButton( $message, $attributes ); 00519 } else { 00520 return ''; 00521 } 00522 } 00523 00536 function historyLine( $row, $next, $notificationtimestamp = false, 00537 $latest = false, $firstInList = false ) 00538 { 00539 $rev = new Revision( $row ); 00540 $rev->setTitle( $this->getTitle() ); 00541 00542 if ( is_object( $next ) ) { 00543 $prevRev = new Revision( $next ); 00544 $prevRev->setTitle( $this->getTitle() ); 00545 } else { 00546 $prevRev = null; 00547 } 00548 00549 $curlink = $this->curLink( $rev, $latest ); 00550 $lastlink = $this->lastLink( $rev, $next ); 00551 $diffButtons = $this->diffButtons( $rev, $firstInList ); 00552 $histLinks = Html::rawElement( 00553 'span', 00554 array( 'class' => 'mw-history-histlinks' ), 00555 $this->msg( 'parentheses' )->rawParams( $curlink . $this->historyPage->message['pipe-separator'] . $lastlink )->escaped() 00556 ); 00557 $s = $histLinks . $diffButtons; 00558 00559 $link = $this->revLink( $rev ); 00560 $classes = array(); 00561 00562 $del = ''; 00563 $user = $this->getUser(); 00564 // Show checkboxes for each revision 00565 if ( $user->isAllowed( 'deleterevision' ) ) { 00566 $this->preventClickjacking(); 00567 // If revision was hidden from sysops, disable the checkbox 00568 if ( !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) { 00569 $del = Xml::check( 'deleterevisions', false, array( 'disabled' => 'disabled' ) ); 00570 // Otherwise, enable the checkbox... 00571 } else { 00572 $del = Xml::check( 'showhiderevisions', false, 00573 array( 'name' => 'ids[' . $rev->getId() . ']' ) ); 00574 } 00575 // User can only view deleted revisions... 00576 } elseif ( $rev->getVisibility() && $user->isAllowed( 'deletedhistory' ) ) { 00577 // If revision was hidden from sysops, disable the link 00578 if ( !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) { 00579 $del = Linker::revDeleteLinkDisabled( false ); 00580 // Otherwise, show the link... 00581 } else { 00582 $query = array( 'type' => 'revision', 00583 'target' => $this->getTitle()->getPrefixedDBkey(), 'ids' => $rev->getId() ); 00584 $del .= Linker::revDeleteLink( $query, 00585 $rev->isDeleted( Revision::DELETED_RESTRICTED ), false ); 00586 } 00587 } 00588 if ( $del ) { 00589 $s .= " $del "; 00590 } 00591 00592 $lang = $this->getLanguage(); 00593 $dirmark = $lang->getDirMark(); 00594 00595 $s .= " $link"; 00596 $s .= $dirmark; 00597 $s .= " <span class='history-user'>" . 00598 Linker::revUserTools( $rev, true ) . "</span>"; 00599 $s .= $dirmark; 00600 00601 if ( $rev->isMinor() ) { 00602 $s .= ' ' . ChangesList::flag( 'minor' ); 00603 } 00604 00605 # Sometimes rev_len isn't populated 00606 if ( $rev->getSize() !== null ) { 00607 # Size is always public data 00608 $prevSize = isset( $this->parentLens[$row->rev_parent_id] ) 00609 ? $this->parentLens[$row->rev_parent_id] 00610 : 0; 00611 $sDiff = ChangesList::showCharacterDifference( $prevSize, $rev->getSize() ); 00612 $fSize = Linker::formatRevisionSize( $rev->getSize() ); 00613 $s .= ' <span class="mw-changeslist-separator">. .</span> ' . "$fSize $sDiff"; 00614 } 00615 00616 # Text following the character difference is added just before running hooks 00617 $s2 = Linker::revComment( $rev, false, true ); 00618 00619 if ( $notificationtimestamp && ( $row->rev_timestamp >= $notificationtimestamp ) ) { 00620 $s2 .= ' <span class="updatedmarker">' . $this->msg( 'updatedmarker' )->escaped() . '</span>'; 00621 $classes[] = 'mw-history-line-updated'; 00622 } 00623 00624 $tools = array(); 00625 00626 # Rollback and undo links 00627 if ( $prevRev && $this->getTitle()->quickUserCan( 'edit', $user ) ) { 00628 if ( $latest && $this->getTitle()->quickUserCan( 'rollback', $user ) ) { 00629 // Get a rollback link without the brackets 00630 $rollbackLink = Linker::generateRollback( $rev, $this->getContext(), array( 'verify', 'noBrackets' ) ); 00631 if ( $rollbackLink ) { 00632 $this->preventClickjacking(); 00633 $tools[] = $rollbackLink; 00634 } 00635 } 00636 00637 if ( !$rev->isDeleted( Revision::DELETED_TEXT ) 00638 && !$prevRev->isDeleted( Revision::DELETED_TEXT ) ) 00639 { 00640 # Create undo tooltip for the first (=latest) line only 00641 $undoTooltip = $latest 00642 ? array( 'title' => $this->msg( 'tooltip-undo' )->text() ) 00643 : array(); 00644 $undolink = Linker::linkKnown( 00645 $this->getTitle(), 00646 $this->msg( 'editundo' )->escaped(), 00647 $undoTooltip, 00648 array( 00649 'action' => 'edit', 00650 'undoafter' => $prevRev->getId(), 00651 'undo' => $rev->getId() 00652 ) 00653 ); 00654 $tools[] = "<span class=\"mw-history-undo\">{$undolink}</span>"; 00655 } 00656 } 00657 // Allow extension to add their own links here 00658 wfRunHooks( 'HistoryRevisionTools', array( $rev, &$tools ) ); 00659 00660 if ( $tools ) { 00661 $s2 .= ' ' . $this->msg( 'parentheses' )->rawParams( $lang->pipeList( $tools ) )->escaped(); 00662 } 00663 00664 # Tags 00665 list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow( $row->ts_tags, 'history' ); 00666 $classes = array_merge( $classes, $newClasses ); 00667 if ( $tagSummary !== '' ) { 00668 $s2 .= " $tagSummary"; 00669 } 00670 00671 # Include separator between character difference and following text 00672 if ( $s2 !== '' ) { 00673 $s .= ' <span class="mw-changeslist-separator">. .</span> ' . $s2; 00674 } 00675 00676 wfRunHooks( 'PageHistoryLineEnding', array( $this, &$row, &$s, &$classes ) ); 00677 00678 $attribs = array(); 00679 if ( $classes ) { 00680 $attribs['class'] = implode( ' ', $classes ); 00681 } 00682 00683 return Xml::tags( 'li', $attribs, $s ) . "\n"; 00684 } 00685 00692 function revLink( $rev ) { 00693 $date = $this->getLanguage()->userTimeAndDate( $rev->getTimestamp(), $this->getUser() ); 00694 $date = htmlspecialchars( $date ); 00695 if ( $rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { 00696 $link = Linker::linkKnown( 00697 $this->getTitle(), 00698 $date, 00699 array( 'class' => 'mw-changeslist-date' ), 00700 array( 'oldid' => $rev->getId() ) 00701 ); 00702 } else { 00703 $link = $date; 00704 } 00705 if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) { 00706 $link = "<span class=\"history-deleted\">$link</span>"; 00707 } 00708 return $link; 00709 } 00710 00718 function curLink( $rev, $latest ) { 00719 $cur = $this->historyPage->message['cur']; 00720 if ( $latest || !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { 00721 return $cur; 00722 } else { 00723 return Linker::linkKnown( 00724 $this->getTitle(), 00725 $cur, 00726 array(), 00727 array( 00728 'diff' => $this->getWikiPage()->getLatest(), 00729 'oldid' => $rev->getId() 00730 ) 00731 ); 00732 } 00733 } 00734 00742 function lastLink( $prevRev, $next ) { 00743 $last = $this->historyPage->message['last']; 00744 # $next may either be a Row, null, or "unkown" 00745 $nextRev = is_object( $next ) ? new Revision( $next ) : $next; 00746 if ( is_null( $next ) ) { 00747 # Probably no next row 00748 return $last; 00749 } elseif ( $next === 'unknown' ) { 00750 # Next row probably exists but is unknown, use an oldid=prev link 00751 return Linker::linkKnown( 00752 $this->getTitle(), 00753 $last, 00754 array(), 00755 array( 00756 'diff' => $prevRev->getId(), 00757 'oldid' => 'prev' 00758 ) 00759 ); 00760 } elseif ( !$prevRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) 00761 || !$nextRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) 00762 { 00763 return $last; 00764 } else { 00765 return Linker::linkKnown( 00766 $this->getTitle(), 00767 $last, 00768 array(), 00769 array( 00770 'diff' => $prevRev->getId(), 00771 'oldid' => $next->rev_id 00772 ) 00773 ); 00774 } 00775 } 00776 00785 function diffButtons( $rev, $firstInList ) { 00786 if ( $this->getNumRows() > 1 ) { 00787 $id = $rev->getId(); 00788 $radio = array( 'type' => 'radio', 'value' => $id ); 00790 if ( $firstInList ) { 00791 $first = Xml::element( 'input', 00792 array_merge( $radio, array( 00793 'style' => 'visibility:hidden', 00794 'name' => 'oldid', 00795 'id' => 'mw-oldid-null' ) ) 00796 ); 00797 $checkmark = array( 'checked' => 'checked' ); 00798 } else { 00799 # Check visibility of old revisions 00800 if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { 00801 $radio['disabled'] = 'disabled'; 00802 $checkmark = array(); // We will check the next possible one 00803 } elseif ( !$this->oldIdChecked ) { 00804 $checkmark = array( 'checked' => 'checked' ); 00805 $this->oldIdChecked = $id; 00806 } else { 00807 $checkmark = array(); 00808 } 00809 $first = Xml::element( 'input', 00810 array_merge( $radio, $checkmark, array( 00811 'name' => 'oldid', 00812 'id' => "mw-oldid-$id" ) ) ); 00813 $checkmark = array(); 00814 } 00815 $second = Xml::element( 'input', 00816 array_merge( $radio, $checkmark, array( 00817 'name' => 'diff', 00818 'id' => "mw-diff-$id" ) ) ); 00819 return $first . $second; 00820 } else { 00821 return ''; 00822 } 00823 } 00824 00828 function preventClickjacking( $enable = true ) { 00829 $this->preventClickjacking = $enable; 00830 } 00831 00836 function getPreventClickjacking() { 00837 return $this->preventClickjacking; 00838 } 00839 } 00840 00844 class HistoryPage extends HistoryAction { 00845 public function __construct( Page $article ) { # Just to make it public 00846 parent::__construct( $article ); 00847 } 00848 00849 public function history() { 00850 $this->onView(); 00851 } 00852 }