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