MediaWiki  REL1_22
HistoryAction.php
Go to the documentation of this file.
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 ) . '&#160;' .
00182             ( $tagSelector ? ( implode( '&#160;', $tagSelector ) . '&#160;' ) : '' ) .
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 }