MediaWiki
master
|
00001 <?php 00034 class HistoryAction extends FormlessAction { 00035 const DIR_PREV = 0; 00036 const DIR_NEXT = 1; 00037 00038 public function getName() { 00039 return 'history'; 00040 } 00041 00042 public function requiresWrite() { 00043 return false; 00044 } 00045 00046 public function requiresUnblock() { 00047 return false; 00048 } 00049 00050 protected function getPageTitle() { 00051 return $this->msg( 'history-title', $this->getTitle()->getPrefixedText() )->text(); 00052 } 00053 00054 protected function getDescription() { 00055 // Creation of a subtitle link pointing to [[Special:Log]] 00056 return Linker::linkKnown( 00057 SpecialPage::getTitleFor( 'Log' ), 00058 $this->msg( 'viewpagelogs' )->escaped(), 00059 array(), 00060 array( 'page' => $this->getTitle()->getPrefixedText() ) 00061 ); 00062 } 00063 00068 public function getArticle() { 00069 return $this->page; 00070 } 00071 00076 private function preCacheMessages() { 00077 // Precache various messages 00078 if ( !isset( $this->message ) ) { 00079 $msgs = array( 'cur', 'last', 'pipe-separator' ); 00080 foreach ( $msgs as $msg ) { 00081 $this->message[$msg] = $this->msg( $msg )->escaped(); 00082 } 00083 } 00084 } 00085 00089 function onView() { 00090 global $wgScript, $wgUseFileCache; 00091 00092 $out = $this->getOutput(); 00093 $request = $this->getRequest(); 00094 00098 if ( $out->checkLastModified( $this->page->getTouched() ) ) { 00099 return; // Client cache fresh and headers sent, nothing more to do. 00100 } 00101 00102 wfProfileIn( __METHOD__ ); 00103 00104 $this->preCacheMessages(); 00105 00106 # Fill in the file cache if not set already 00107 if ( $wgUseFileCache && HTMLFileCache::useFileCache( $this->getContext() ) ) { 00108 $cache = HTMLFileCache::newFromTitle( $this->getTitle(), 'history' ); 00109 if ( !$cache->isCacheGood( /* Assume up to date */ ) ) { 00110 ob_start( array( &$cache, 'saveToFileCache' ) ); 00111 } 00112 } 00113 00114 // Setup page variables. 00115 $out->setFeedAppendQuery( 'action=history' ); 00116 $out->addModules( array( 'mediawiki.legacy.history', 'mediawiki.action.history' ) ); 00117 00118 // Handle atom/RSS feeds. 00119 $feedType = $request->getVal( 'feed' ); 00120 if ( $feedType ) { 00121 $this->feed( $feedType ); 00122 wfProfileOut( __METHOD__ ); 00123 return; 00124 } 00125 00126 // Fail nicely if article doesn't exist. 00127 if ( !$this->page->exists() ) { 00128 $out->addWikiMsg( 'nohistory' ); 00129 # show deletion/move log if there is an entry 00130 LogEventsList::showLogExtract( 00131 $out, 00132 array( 'delete', 'move' ), 00133 $this->getTitle(), 00134 '', 00135 array( 'lim' => 10, 00136 'conds' => array( "log_action != 'revision'" ), 00137 'showIfEmpty' => false, 00138 'msgKey' => array( 'moveddeleted-notice' ) 00139 ) 00140 ); 00141 wfProfileOut( __METHOD__ ); 00142 return; 00143 } 00144 00148 $year = $request->getInt( 'year' ); 00149 $month = $request->getInt( 'month' ); 00150 $tagFilter = $request->getVal( 'tagfilter' ); 00151 $tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter ); 00152 00156 if ( $request->getBool( 'deleted' ) ) { 00157 $conds = array( "rev_deleted != '0'" ); 00158 } else { 00159 $conds = array(); 00160 } 00161 if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) { 00162 $checkDeleted = Xml::checkLabel( $this->msg( 'history-show-deleted' )->text(), 00163 'deleted', 'mw-show-deleted-only', $request->getBool( 'deleted' ) ) . "\n"; 00164 } else { 00165 $checkDeleted = ''; 00166 } 00167 00168 // Add the general form 00169 $action = htmlspecialchars( $wgScript ); 00170 $out->addHTML( 00171 "<form action=\"$action\" method=\"get\" id=\"mw-history-searchform\">" . 00172 Xml::fieldset( 00173 $this->msg( 'history-fieldset-title' )->text(), 00174 false, 00175 array( 'id' => 'mw-history-search' ) 00176 ) . 00177 Html::hidden( 'title', $this->getTitle()->getPrefixedDBKey() ) . "\n" . 00178 Html::hidden( 'action', 'history' ) . "\n" . 00179 Xml::dateMenu( $year, $month ) . ' ' . 00180 ( $tagSelector ? ( implode( ' ', $tagSelector ) . ' ' ) : '' ) . 00181 $checkDeleted . 00182 Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ) . "\n" . 00183 '</fieldset></form>' 00184 ); 00185 00186 wfRunHooks( 'PageHistoryBeforeList', array( &$this->page ) ); 00187 00188 // Create and output the list. 00189 $pager = new HistoryPager( $this, $year, $month, $tagFilter, $conds ); 00190 $out->addHTML( 00191 $pager->getNavigationBar() . 00192 $pager->getBody() . 00193 $pager->getNavigationBar() 00194 ); 00195 $out->preventClickjacking( $pager->getPreventClickjacking() ); 00196 00197 wfProfileOut( __METHOD__ ); 00198 } 00199 00210 function fetchRevisions( $limit, $offset, $direction ) { 00211 // Fail if article doesn't exist. 00212 if( !$this->getTitle()->exists() ) { 00213 return new FakeResultWrapper( array() ); 00214 } 00215 00216 $dbr = wfGetDB( DB_SLAVE ); 00217 00218 if ( $direction == HistoryPage::DIR_PREV ) { 00219 list( $dirs, $oper ) = array( "ASC", ">=" ); 00220 } else { /* $direction == HistoryPage::DIR_NEXT */ 00221 list( $dirs, $oper ) = array( "DESC", "<=" ); 00222 } 00223 00224 if ( $offset ) { 00225 $offsets = array( "rev_timestamp $oper '$offset'" ); 00226 } else { 00227 $offsets = array(); 00228 } 00229 00230 $page_id = $this->page->getId(); 00231 00232 return $dbr->select( 'revision', 00233 Revision::selectFields(), 00234 array_merge( array( "rev_page=$page_id" ), $offsets ), 00235 __METHOD__, 00236 array( 'ORDER BY' => "rev_timestamp $dirs", 00237 'USE INDEX' => 'page_timestamp', 'LIMIT' => $limit ) 00238 ); 00239 } 00240 00246 function feed( $type ) { 00247 global $wgFeedClasses, $wgFeedLimit; 00248 if ( !FeedUtils::checkFeedOutput( $type ) ) { 00249 return; 00250 } 00251 $request = $this->getRequest(); 00252 00253 $feed = new $wgFeedClasses[$type]( 00254 $this->getTitle()->getPrefixedText() . ' - ' . 00255 $this->msg( 'history-feed-title' )->inContentLanguage()->text(), 00256 $this->msg( 'history-feed-description' )->inContentLanguage()->text(), 00257 $this->getTitle()->getFullUrl( 'action=history' ) 00258 ); 00259 00260 // Get a limit on number of feed entries. Provide a sane default 00261 // of 10 if none is defined (but limit to $wgFeedLimit max) 00262 $limit = $request->getInt( 'limit', 10 ); 00263 if ( $limit > $wgFeedLimit || $limit < 1 ) { 00264 $limit = 10; 00265 } 00266 $items = $this->fetchRevisions( $limit, 0, HistoryPage::DIR_NEXT ); 00267 00268 // Generate feed elements enclosed between header and footer. 00269 $feed->outHeader(); 00270 if ( $items->numRows() ) { 00271 foreach ( $items as $row ) { 00272 $feed->outItem( $this->feedItem( $row ) ); 00273 } 00274 } else { 00275 $feed->outItem( $this->feedEmpty() ); 00276 } 00277 $feed->outFooter(); 00278 } 00279 00280 function feedEmpty() { 00281 return new FeedItem( 00282 $this->msg( 'nohistory' )->inContentLanguage()->text(), 00283 $this->msg( 'history-feed-empty' )->inContentLanguage()->parseAsBlock(), 00284 $this->getTitle()->getFullUrl(), 00285 wfTimestamp( TS_MW ), 00286 '', 00287 $this->getTitle()->getTalkPage()->getFullUrl() 00288 ); 00289 } 00290 00299 function feedItem( $row ) { 00300 $rev = new Revision( $row ); 00301 $rev->setTitle( $this->getTitle() ); 00302 $text = FeedUtils::formatDiffRow( 00303 $this->getTitle(), 00304 $this->getTitle()->getPreviousRevisionID( $rev->getId() ), 00305 $rev->getId(), 00306 $rev->getTimestamp(), 00307 $rev->getComment() 00308 ); 00309 if ( $rev->getComment() == '' ) { 00310 global $wgContLang; 00311 $title = $this->msg( 'history-feed-item-nocomment', 00312 $rev->getUserText(), 00313 $wgContLang->timeanddate( $rev->getTimestamp() ), 00314 $wgContLang->date( $rev->getTimestamp() ), 00315 $wgContLang->time( $rev->getTimestamp() ) )->inContentLanguage()->text(); 00316 } else { 00317 $title = $rev->getUserText() . 00318 $this->msg( 'colon-separator' )->inContentLanguage()->text() . 00319 FeedItem::stripComment( $rev->getComment() ); 00320 } 00321 return new FeedItem( 00322 $title, 00323 $text, 00324 $this->getTitle()->getFullUrl( 'diff=' . $rev->getId() . '&oldid=prev' ), 00325 $rev->getTimestamp(), 00326 $rev->getUserText(), 00327 $this->getTitle()->getTalkPage()->getFullUrl() 00328 ); 00329 } 00330 } 00331 00335 class HistoryPager extends ReverseChronologicalPager { 00336 public $lastRow = false, $counter, $historyPage, $buttons, $conds; 00337 protected $oldIdChecked; 00338 protected $preventClickjacking = false; 00342 protected $parentLens; 00343 00344 function __construct( $historyPage, $year = '', $month = '', $tagFilter = '', $conds = array() ) { 00345 parent::__construct( $historyPage->getContext() ); 00346 $this->historyPage = $historyPage; 00347 $this->tagFilter = $tagFilter; 00348 $this->getDateCond( $year, $month ); 00349 $this->conds = $conds; 00350 } 00351 00352 // For hook compatibility... 00353 function getArticle() { 00354 return $this->historyPage->getArticle(); 00355 } 00356 00357 function getSqlComment() { 00358 if ( $this->conds ) { 00359 return 'history page filtered'; // potentially slow, see CR r58153 00360 } else { 00361 return 'history page unfiltered'; 00362 } 00363 } 00364 00365 function getQueryInfo() { 00366 $queryInfo = array( 00367 'tables' => array( 'revision', 'user' ), 00368 'fields' => array_merge( Revision::selectFields(), Revision::selectUserFields() ), 00369 'conds' => array_merge( 00370 array( 'rev_page' => $this->getWikiPage()->getId() ), 00371 $this->conds ), 00372 'options' => array( 'USE INDEX' => array( 'revision' => 'page_timestamp' ) ), 00373 'join_conds' => array( 00374 'user' => Revision::userJoinCond(), 00375 'tag_summary' => array( 'LEFT JOIN', 'ts_rev_id=rev_id' ) ), 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 # Size is always public data 00606 $prevSize = isset( $this->parentLens[$row->rev_parent_id] ) 00607 ? $this->parentLens[$row->rev_parent_id] 00608 : 0; 00609 $sDiff = ChangesList::showCharacterDifference( $prevSize, $rev->getSize() ); 00610 $fSize = Linker::formatRevisionSize($rev->getSize()); 00611 $s .= ' <span class="mw-changeslist-separator">. .</span> ' . "$fSize $sDiff"; 00612 00613 # Text following the character difference is added just before running hooks 00614 $s2 = Linker::revComment( $rev, false, true ); 00615 00616 if ( $notificationtimestamp && ( $row->rev_timestamp >= $notificationtimestamp ) ) { 00617 $s2 .= ' <span class="updatedmarker">' . $this->msg( 'updatedmarker' )->escaped() . '</span>'; 00618 $classes[] = 'mw-history-line-updated'; 00619 } 00620 00621 $tools = array(); 00622 00623 # Rollback and undo links 00624 if ( $prevRev && $this->getTitle()->quickUserCan( 'edit', $user ) ) { 00625 if ( $latest && $this->getTitle()->quickUserCan( 'rollback', $user ) ) { 00626 $this->preventClickjacking(); 00627 $tools[] = '<span class="mw-rollback-link">' . 00628 Linker::buildRollbackLink( $rev, $this->getContext() ) . '</span>'; 00629 } 00630 00631 if ( !$rev->isDeleted( Revision::DELETED_TEXT ) 00632 && !$prevRev->isDeleted( Revision::DELETED_TEXT ) ) 00633 { 00634 # Create undo tooltip for the first (=latest) line only 00635 $undoTooltip = $latest 00636 ? array( 'title' => $this->msg( 'tooltip-undo' )->text() ) 00637 : array(); 00638 $undolink = Linker::linkKnown( 00639 $this->getTitle(), 00640 $this->msg( 'editundo' )->escaped(), 00641 $undoTooltip, 00642 array( 00643 'action' => 'edit', 00644 'undoafter' => $prevRev->getId(), 00645 'undo' => $rev->getId() 00646 ) 00647 ); 00648 $tools[] = "<span class=\"mw-history-undo\">{$undolink}</span>"; 00649 } 00650 } 00651 00652 if ( $tools ) { 00653 $s2 .= ' '. $this->msg( 'parentheses' )->rawParams( $lang->pipeList( $tools ) )->escaped(); 00654 } 00655 00656 # Tags 00657 list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow( $row->ts_tags, 'history' ); 00658 $classes = array_merge( $classes, $newClasses ); 00659 if ( $tagSummary !== '' ) { 00660 $s2 .= " $tagSummary"; 00661 } 00662 00663 # Include separator between character difference and following text 00664 if ( $s2 !== '' ) { 00665 $s .= ' <span class="mw-changeslist-separator">. .</span> ' . $s2; 00666 } 00667 00668 wfRunHooks( 'PageHistoryLineEnding', array( $this, &$row , &$s, &$classes ) ); 00669 00670 $attribs = array(); 00671 if ( $classes ) { 00672 $attribs['class'] = implode( ' ', $classes ); 00673 } 00674 00675 return Xml::tags( 'li', $attribs, $s ) . "\n"; 00676 } 00677 00684 function revLink( $rev ) { 00685 $date = $this->getLanguage()->userTimeAndDate( $rev->getTimestamp(), $this->getUser() ); 00686 $date = htmlspecialchars( $date ); 00687 if ( $rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { 00688 $link = Linker::linkKnown( 00689 $this->getTitle(), 00690 $date, 00691 array( 'class' => 'mw-changeslist-date' ), 00692 array( 'oldid' => $rev->getId() ) 00693 ); 00694 } else { 00695 $link = $date; 00696 } 00697 if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) { 00698 $link = "<span class=\"history-deleted\">$link</span>"; 00699 } 00700 return $link; 00701 } 00702 00710 function curLink( $rev, $latest ) { 00711 $cur = $this->historyPage->message['cur']; 00712 if ( $latest || !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { 00713 return $cur; 00714 } else { 00715 return Linker::linkKnown( 00716 $this->getTitle(), 00717 $cur, 00718 array(), 00719 array( 00720 'diff' => $this->getWikiPage()->getLatest(), 00721 'oldid' => $rev->getId() 00722 ) 00723 ); 00724 } 00725 } 00726 00734 function lastLink( $prevRev, $next ) { 00735 $last = $this->historyPage->message['last']; 00736 # $next may either be a Row, null, or "unkown" 00737 $nextRev = is_object( $next ) ? new Revision( $next ) : $next; 00738 if ( is_null( $next ) ) { 00739 # Probably no next row 00740 return $last; 00741 } elseif ( $next === 'unknown' ) { 00742 # Next row probably exists but is unknown, use an oldid=prev link 00743 return Linker::linkKnown( 00744 $this->getTitle(), 00745 $last, 00746 array(), 00747 array( 00748 'diff' => $prevRev->getId(), 00749 'oldid' => 'prev' 00750 ) 00751 ); 00752 } elseif ( !$prevRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) 00753 || !$nextRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) 00754 { 00755 return $last; 00756 } else { 00757 return Linker::linkKnown( 00758 $this->getTitle(), 00759 $last, 00760 array(), 00761 array( 00762 'diff' => $prevRev->getId(), 00763 'oldid' => $next->rev_id 00764 ) 00765 ); 00766 } 00767 } 00768 00777 function diffButtons( $rev, $firstInList ) { 00778 if ( $this->getNumRows() > 1 ) { 00779 $id = $rev->getId(); 00780 $radio = array( 'type' => 'radio', 'value' => $id ); 00782 if ( $firstInList ) { 00783 $first = Xml::element( 'input', 00784 array_merge( $radio, array( 00785 'style' => 'visibility:hidden', 00786 'name' => 'oldid', 00787 'id' => 'mw-oldid-null' ) ) 00788 ); 00789 $checkmark = array( 'checked' => 'checked' ); 00790 } else { 00791 # Check visibility of old revisions 00792 if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { 00793 $radio['disabled'] = 'disabled'; 00794 $checkmark = array(); // We will check the next possible one 00795 } elseif ( !$this->oldIdChecked ) { 00796 $checkmark = array( 'checked' => 'checked' ); 00797 $this->oldIdChecked = $id; 00798 } else { 00799 $checkmark = array(); 00800 } 00801 $first = Xml::element( 'input', 00802 array_merge( $radio, $checkmark, array( 00803 'name' => 'oldid', 00804 'id' => "mw-oldid-$id" ) ) ); 00805 $checkmark = array(); 00806 } 00807 $second = Xml::element( 'input', 00808 array_merge( $radio, $checkmark, array( 00809 'name' => 'diff', 00810 'id' => "mw-diff-$id" ) ) ); 00811 return $first . $second; 00812 } else { 00813 return ''; 00814 } 00815 } 00816 00820 function preventClickjacking( $enable = true ) { 00821 $this->preventClickjacking = $enable; 00822 } 00823 00828 function getPreventClickjacking() { 00829 return $this->preventClickjacking; 00830 } 00831 } 00832 00836 class HistoryPage extends HistoryAction { 00837 public function __construct( Page $article ) { # Just to make it public 00838 parent::__construct( $article ); 00839 } 00840 00841 public function history() { 00842 $this->onView(); 00843 } 00844 }