85 parent::__construct( $historyPage->
getContext() );
86 $this->historyPage = $historyPage;
89 $this->conds = $conds;
91 $services = MediaWikiServices::getInstance();
92 $this->revisionStore = $services->getRevisionStore();
95 ?? $services->getWatchlistManager();
100 return $this->historyPage->getArticle();
104 if ( $this->conds ) {
105 return 'history page filtered';
107 return 'history page unfiltered';
112 $revQuery = $this->revisionStore->getQueryInfo( [
'user' ] );
114 $revIndex = $this->mDb->indexExists(
'revision',
'page_timestamp', __METHOD__ )
116 :
'rev_page_timestamp';
121 'conds' => array_merge(
124 'options' => [
'USE INDEX' => [
'revision' => $revIndex ] ],
128 $queryInfo[
'tables'],
129 $queryInfo[
'fields'],
131 $queryInfo[
'join_conds'],
132 $queryInfo[
'options'],
136 $this->getHookRunner()->onPageHistoryPager__getQueryInfo( $this, $queryInfo );
142 return [ [
'rev_timestamp',
'rev_id' ] ];
150 if ( $this->lastRow ) {
151 $firstInList = $this->counter == 1;
154 $notifTimestamp = $this->
getConfig()->get(
'ShowUpdatedMarker' )
155 ? $this->watchlistManager
159 $s = $this->
historyLine( $this->lastRow, $row, $notifTimestamp,
false, $firstInList );
163 $this->lastRow = $row;
169 if ( !$this->getHookRunner()->onPageHistoryPager__doBatchLookups( $this, $this->mResult ) ) {
173 # Do a link batch query
174 $this->mResult->seek( 0 );
175 $batch = $this->linkBatchFactory->newLinkBatch();
177 foreach ( $this->mResult as $row ) {
178 if ( $row->rev_parent_id ) {
179 $revIds[] = $row->rev_parent_id;
181 if ( $row->user_name !==
null ) {
182 $batch->add(
NS_USER, $row->user_name );
184 }
else { #
for anons or usernames of imported revisions
185 $batch->add(
NS_USER, $row->rev_user_text );
189 $this->parentLens = $this->revisionStore->getRevisionSizes( $revIds );
191 $this->mResult->seek( 0 );
199 return $this->
msg(
'history-empty' )->escaped();
208 $this->lastRow =
false;
210 $this->oldIdChecked = 0;
215 $this->
getOutput()->wrapWikiMsg(
"<div class='mw-history-legend'>\n$1\n</div>",
'histlegend' );
216 $s = Html::openElement(
'form', [
218 'id' =>
'mw-history-compare'
220 $s .= Html::hidden(
'title', $this->
getTitle()->getPrefixedDBkey() ) .
"\n";
221 $s .= Html::hidden(
'action',
'historysubmit' ) .
"\n";
222 $s .= Html::hidden(
'type',
'revision' ) .
"\n";
224 $this->buttons .= Html::openElement(
225 'div', [
'class' =>
'mw-history-compareselectedversions' ] );
226 $className =
'historysubmit mw-history-compareselectedversions-button mw-ui-button';
227 $attrs = [
'class' => $className ]
229 $this->buttons .= $this->
submitButton( $this->
msg(
'compareselectedversions' )->text(),
234 if ( $this->
getAuthority()->isAllowed(
'deleterevision' ) ) {
236 'revisiondelete',
'showhideselectedversions' );
238 if ( $this->showTagEditUI ) {
240 'editchangetags',
'history-edit-tags' );
242 if ( $actionButtons ) {
243 $this->buttons .= Xml::tags(
'div', [
'class' =>
244 'mw-history-revisionactions' ], $actionButtons );
247 if ( $this->
getAuthority()->isAllowed(
'deleterevision' ) || $this->showTagEditUI ) {
251 $this->buttons .=
'</div>';
253 $s .= $this->buttons;
254 $s .=
'<ul id="pagehistory">' .
"\n";
262 $element = Html::element(
268 'class' =>
"historysubmit mw-history-$name-button mw-ui-button",
270 $this->
msg( $msg )->text()
280 if ( $this->lastRow ) {
281 $firstInList = $this->counter == 1;
282 if ( $this->mIsBackwards ) {
283 # Next row is unknown, but for UI reasons, probably exists if an offset has been specified
284 if ( $this->mOffset ==
'' ) {
290 # The next row is the past-the-end row
291 $next = $this->mPastTheEndRow;
295 $notifTimestamp = $this->
getConfig()->get(
'ShowUpdatedMarker' )
296 ? $this->watchlistManager
300 $s = $this->
historyLine( $this->lastRow, $next, $notifTimestamp,
false, $firstInList );
305 # Add second buttons only if there is more than one rev
307 $s .= $this->buttons;
321 # Disable submit button if history has 1 revision only
323 return Html::submitButton( $message, $attributes );
343 private function historyLine( $row, $next, $notificationtimestamp =
false,
344 $dummy =
false, $firstInList =
false ) {
345 $revRecord = $this->revisionStore->newRevisionFromRow(
347 RevisionStore::READ_NORMAL,
351 if ( is_object( $next ) ) {
352 $previousRevRecord = $this->revisionStore->newRevisionFromRow(
354 RevisionStore::READ_NORMAL,
358 $previousRevRecord =
null;
361 $latest = $revRecord->getId() === $this->
getWikiPage()->getLatest();
362 $curlink = $this->
curLink( $revRecord );
363 $lastlink = $this->
lastLink( $revRecord, $next );
364 $curLastlinks = Html::rawElement(
'span', [], $curlink ) .
365 Html::rawElement(
'span', [], $lastlink );
366 $histLinks = Html::rawElement(
368 [
'class' =>
'mw-history-histlinks mw-changeslist-links' ],
372 $diffButtons = $this->
diffButtons( $revRecord, $firstInList );
373 $s = $histLinks . $diffButtons;
375 $link = $this->
revLink( $revRecord );
380 $canRevDelete = $this->
getAuthority()->isAllowed(
'deleterevision' );
383 $visibility = $revRecord->getVisibility();
384 if ( $canRevDelete || $this->showTagEditUI ) {
388 if ( !$this->showTagEditUI
389 && !$revRecord->userCan( RevisionRecord::DELETED_RESTRICTED, $this->getAuthority() )
391 $del = Xml::check(
'deleterevisions',
false, [
'disabled' =>
'disabled' ] );
394 $del = Xml::check(
'showhiderevisions',
false,
395 [
'name' =>
'ids[' . $revRecord->getId() .
']' ] );
398 } elseif ( $revRecord->getVisibility() && $this->getAuthority()->isAllowed(
'deletedhistory' ) ) {
400 if ( !$revRecord->userCan( RevisionRecord::DELETED_RESTRICTED, $this->getAuthority() ) ) {
405 'type' =>
'revision',
406 'target' => $this->
getTitle()->getPrefixedDBkey(),
407 'ids' => $revRecord->getId()
411 $revRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ),
421 $dirmark =
$lang->getDirMark();
425 $s .=
" <span class='history-user'>" .
429 if ( $revRecord->isMinor() ) {
430 $s .=
' ' . ChangesList::flag(
'minor', $this->
getContext() );
433 # Sometimes rev_len isn't populated
434 if ( $revRecord->getSize() !==
null ) {
435 # Size is always public data
436 $prevSize = $this->parentLens[$row->rev_parent_id] ?? 0;
437 $sDiff = ChangesList::showCharacterDifference( $prevSize, $revRecord->getSize() );
439 $s .=
' <span class="mw-changeslist-separator"></span> ' .
"$fSize $sDiff";
442 # Text following the character difference is added just before running hooks
445 if ( $notificationtimestamp && ( $row->rev_timestamp >= $notificationtimestamp ) ) {
446 $s2 .=
' <span class="updatedmarker">' . $this->
msg(
'updatedmarker' )->escaped() .
'</span>';
447 $classes[] =
'mw-history-line-updated';
452 # Rollback and undo links
461 [
'verify',
'noBrackets' ]
463 if ( $rollbackLink ) {
465 $tools[] = $rollbackLink;
469 if ( !$revRecord->isDeleted( RevisionRecord::DELETED_TEXT )
470 && !$previousRevRecord->isDeleted( RevisionRecord::DELETED_TEXT )
472 # Create undo tooltip for the first (=latest) line only
473 $undoTooltip = $latest
474 ? [
'title' => $this->
msg(
'tooltip-undo' )->text() ]
478 $this->
msg(
'editundo' )->text(),
482 'undoafter' => $previousRevRecord->getId(),
483 'undo' => $revRecord->getId()
486 $tools[] =
"<span class=\"mw-history-undo\">{$undolink}</span>";
490 $this->getHookRunner()->onHistoryTools(
498 $s2 .=
' ' . Html::openElement(
'span', [
'class' =>
'mw-changeslist-links' ] );
499 foreach ( $tools as $tool ) {
500 $s2 .= Html::rawElement(
'span', [], $tool );
502 $s2 .= Html::closeElement(
'span' );
511 $classes = array_merge( $classes, $newClasses );
512 if ( $tagSummary !==
'' ) {
513 $s2 .=
" $tagSummary";
516 # Include separator between character difference and following text
518 $s .=
' <span class="mw-changeslist-separator"></span> ' . $s2;
521 $attribs = [
'data-mw-revid' => $revRecord->getId() ];
523 $this->getHookRunner()->onPageHistoryLineEnding( $this, $row,
$s, $classes, $attribs );
524 $attribs = array_filter( $attribs,
525 [ Sanitizer::class,
'isReservedDataAttribute' ],
530 $attribs[
'class'] = implode(
' ', $classes );
533 return Xml::tags(
'li', $attribs,
$s ) .
"\n";
554 $cur = $this->historyPage->message[
'cur'];
556 if ( $latest === $rev->
getId()
557 || !$rev->
userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() )
567 'oldid' => $rev->
getId()
583 $last = $this->historyPage->message[
'last'];
585 if ( $next ===
null ) {
586 # Probably no next row
591 if ( $next ===
'unknown' ) {
592 # Next row probably exists but is unknown, use an oldid=prev link
598 'diff' => $prevRev->
getId(),
604 $nextRev = $this->revisionStore->newRevisionFromRow(
606 RevisionStore::READ_NORMAL,
610 if ( !$prevRev->
userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ||
611 !$nextRev->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() )
621 'diff' => $prevRev->
getId(),
622 'oldid' => $next->rev_id
638 $radio = [
'type' =>
'radio',
'value' => $id ];
640 if ( $firstInList ) {
641 $first = Xml::element(
'input',
642 array_merge( $radio, [
643 'style' =>
'visibility:hidden',
645 'id' =>
'mw-oldid-null' ] )
647 $checkmark = [
'checked' =>
'checked' ];
649 # Check visibility of old revisions
650 if ( !$rev->
userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
651 $radio[
'disabled'] =
'disabled';
653 } elseif ( !$this->oldIdChecked ) {
654 $checkmark = [
'checked' =>
'checked' ];
655 $this->oldIdChecked = $id;
659 $first = Xml::element(
'input',
660 array_merge( $radio, $checkmark, [
662 'id' =>
"mw-oldid-$id" ] ) );
665 $second = Xml::element(
'input',
666 array_merge( $radio, $checkmark, [
668 'id' =>
"mw-diff-$id" ] ) );
670 return $first . $second;
685 return parent::isNavigationBarShown();
692 parent::getDefaultQuery();
693 unset( $this->mDefaultQuery[
'date-range-to'] );
694 return $this->mDefaultQuery;
710 return $this->preventClickjacking;
WatchlistManager $watchlistManager
wfScript( $script='index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
getContext()
Get the IContextSource in use here.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
getWikiPage()
Get the WikiPage object.
This class handles printing the history page for an article.
Marks HTML that shouldn't be escaped.
static revComment(RevisionRecord $revRecord, $local=false, $isPublic=false, $useParentheses=true)
Wrap and format the given revision's comment block, if the current user is allowed to view it.
static revDeleteLinkDisabled( $delete=true)
Creates a dead (show/hide) link for deleting revisions/log entries.
static generateRollback(RevisionRecord $revRecord, IContextSource $context=null, $options=[ 'verify'])
Generate a rollback link for a given revision.
static formatRevisionSize( $size)
static revUserTools(RevisionRecord $revRecord, $isPublic=false, $useParentheses=true)
Generate a user tool link cluster if the current user is allowed to view it.
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[], $options=null)
Returns the attributes for the tooltip and access key.
static revDeleteLink( $query=[], $restricted=false, $delete=true)
Creates a (show/hide) link for deleting revisions/log entries.
Class for generating clickable toggle links for a list of checkboxes.
foreach( $mmfl['setupFiles'] as $fileName) if($queue) if(empty( $mmfl['quiet'])) $s
if(!isset( $args[0])) $lang