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' ] );
116 'conds' => array_merge(
119 'options' => [
'USE INDEX' => [
'revision' =>
'page_timestamp' ] ],
123 $queryInfo[
'tables'],
124 $queryInfo[
'fields'],
126 $queryInfo[
'join_conds'],
127 $queryInfo[
'options'],
131 $this->getHookRunner()->onPageHistoryPager__getQueryInfo( $this, $queryInfo );
137 return [ [
'rev_timestamp',
'rev_id' ] ];
145 if ( $this->lastRow ) {
146 $firstInList = $this->counter == 1;
149 $notifTimestamp = $this->
getConfig()->get(
'ShowUpdatedMarker' )
150 ? $this->watchlistManager
154 $s = $this->
historyLine( $this->lastRow, $row, $notifTimestamp,
false, $firstInList );
158 $this->lastRow = $row;
164 if ( !$this->getHookRunner()->onPageHistoryPager__doBatchLookups( $this, $this->mResult ) ) {
168 # Do a link batch query
169 $this->mResult->seek( 0 );
170 $batch = $this->linkBatchFactory->newLinkBatch();
172 foreach ( $this->mResult as $row ) {
173 if ( $row->rev_parent_id ) {
174 $revIds[] = $row->rev_parent_id;
176 if ( $row->user_name !==
null ) {
177 $batch->add(
NS_USER, $row->user_name );
179 }
else { #
for anons or usernames of imported revisions
180 $batch->add(
NS_USER, $row->rev_user_text );
184 $this->parentLens = $this->revisionStore->getRevisionSizes( $revIds );
186 $this->mResult->seek( 0 );
194 return $this->
msg(
'history-empty' )->escaped();
203 $this->lastRow =
false;
205 $this->oldIdChecked = 0;
210 $this->
getOutput()->wrapWikiMsg(
"<div class='mw-history-legend'>\n$1\n</div>",
'histlegend' );
211 $s = Html::openElement(
'form', [
213 'id' =>
'mw-history-compare'
215 $s .= Html::hidden(
'title', $this->
getTitle()->getPrefixedDBkey() ) .
"\n";
216 $s .= Html::hidden(
'action',
'historysubmit' ) .
"\n";
217 $s .= Html::hidden(
'type',
'revision' ) .
"\n";
219 $this->buttons .= Html::openElement(
220 'div', [
'class' =>
'mw-history-compareselectedversions' ] );
221 $className =
'historysubmit mw-history-compareselectedversions-button mw-ui-button';
222 $attrs = [
'class' => $className ]
224 $this->buttons .= $this->
submitButton( $this->
msg(
'compareselectedversions' )->text(),
229 if ( $this->
getAuthority()->isAllowed(
'deleterevision' ) ) {
231 'revisiondelete',
'showhideselectedversions' );
233 if ( $this->showTagEditUI ) {
235 'editchangetags',
'history-edit-tags' );
237 if ( $actionButtons ) {
238 $this->buttons .= Xml::tags(
'div', [
'class' =>
239 'mw-history-revisionactions' ], $actionButtons );
242 if ( $this->
getAuthority()->isAllowed(
'deleterevision' ) || $this->showTagEditUI ) {
246 $this->buttons .=
'</div>';
248 $s .= $this->buttons;
249 $s .=
'<ul id="pagehistory">' .
"\n";
257 $element = Html::element(
263 'class' =>
"historysubmit mw-history-$name-button mw-ui-button",
265 $this->
msg( $msg )->text()
275 if ( $this->lastRow ) {
276 $firstInList = $this->counter == 1;
277 if ( $this->mIsBackwards ) {
278 # Next row is unknown, but for UI reasons, probably exists if an offset has been specified
279 if ( $this->mOffset ==
'' ) {
285 # The next row is the past-the-end row
286 $next = $this->mPastTheEndRow;
290 $notifTimestamp = $this->
getConfig()->get(
'ShowUpdatedMarker' )
291 ? $this->watchlistManager
295 $s = $this->
historyLine( $this->lastRow, $next, $notifTimestamp,
false, $firstInList );
300 # Add second buttons only if there is more than one rev
302 $s .= $this->buttons;
316 # Disable submit button if history has 1 revision only
318 return Html::submitButton( $message, $attributes );
338 private function historyLine( $row, $next, $notificationtimestamp =
false,
339 $dummy =
false, $firstInList =
false ) {
340 $revRecord = $this->revisionStore->newRevisionFromRow(
342 RevisionStore::READ_NORMAL,
346 if ( is_object( $next ) ) {
347 $previousRevRecord = $this->revisionStore->newRevisionFromRow(
349 RevisionStore::READ_NORMAL,
353 $previousRevRecord =
null;
356 $latest = $revRecord->getId() === $this->
getWikiPage()->getLatest();
357 $curlink = $this->
curLink( $revRecord );
358 $lastlink = $this->
lastLink( $revRecord, $next );
359 $curLastlinks = Html::rawElement(
'span', [], $curlink ) .
360 Html::rawElement(
'span', [], $lastlink );
361 $histLinks = Html::rawElement(
363 [
'class' =>
'mw-history-histlinks mw-changeslist-links' ],
367 $diffButtons = $this->
diffButtons( $revRecord, $firstInList );
368 $s = $histLinks . $diffButtons;
370 $link = $this->
revLink( $revRecord );
375 $canRevDelete = $this->
getAuthority()->isAllowed(
'deleterevision' );
378 $visibility = $revRecord->getVisibility();
379 if ( $canRevDelete || $this->showTagEditUI ) {
383 if ( !$this->showTagEditUI
384 && !$revRecord->userCan( RevisionRecord::DELETED_RESTRICTED, $this->getAuthority() )
386 $del = Xml::check(
'deleterevisions',
false, [
'disabled' =>
'disabled' ] );
389 $del = Xml::check(
'showhiderevisions',
false,
390 [
'name' =>
'ids[' . $revRecord->getId() .
']' ] );
393 } elseif ( $revRecord->getVisibility() && $this->getAuthority()->isAllowed(
'deletedhistory' ) ) {
395 if ( !$revRecord->userCan( RevisionRecord::DELETED_RESTRICTED, $this->getAuthority() ) ) {
400 'type' =>
'revision',
401 'target' => $this->
getTitle()->getPrefixedDBkey(),
402 'ids' => $revRecord->getId()
406 $revRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ),
416 $dirmark =
$lang->getDirMark();
420 $s .=
" <span class='history-user'>" .
424 if ( $revRecord->isMinor() ) {
425 $s .=
' ' . ChangesList::flag(
'minor', $this->
getContext() );
428 # Sometimes rev_len isn't populated
429 if ( $revRecord->getSize() !==
null ) {
430 # Size is always public data
431 $prevSize = $this->parentLens[$row->rev_parent_id] ?? 0;
432 $sDiff = ChangesList::showCharacterDifference( $prevSize, $revRecord->getSize() );
434 $s .=
' <span class="mw-changeslist-separator"></span> ' .
"$fSize $sDiff";
437 # Text following the character difference is added just before running hooks
440 if ( $notificationtimestamp && ( $row->rev_timestamp >= $notificationtimestamp ) ) {
441 $s2 .=
' <span class="updatedmarker">' . $this->
msg(
'updatedmarker' )->escaped() .
'</span>';
442 $classes[] =
'mw-history-line-updated';
447 # Rollback and undo links
456 [
'verify',
'noBrackets' ]
458 if ( $rollbackLink ) {
460 $tools[] = $rollbackLink;
464 if ( !$revRecord->isDeleted( RevisionRecord::DELETED_TEXT )
465 && !$previousRevRecord->isDeleted( RevisionRecord::DELETED_TEXT )
467 # Create undo tooltip for the first (=latest) line only
468 $undoTooltip = $latest
469 ? [
'title' => $this->
msg(
'tooltip-undo' )->text() ]
473 $this->
msg(
'editundo' )->text(),
477 'undoafter' => $previousRevRecord->getId(),
478 'undo' => $revRecord->getId()
481 $tools[] =
"<span class=\"mw-history-undo\">{$undolink}</span>";
485 $this->getHookRunner()->onHistoryTools(
493 if ( $this->getHookContainer()->isRegistered(
'HistoryRevisionTools' ) ) {
495 $this->getHookRunner()->onHistoryRevisionTools(
498 $previousRevRecord ?
new Revision( $previousRevRecord ) :
null,
504 $s2 .=
' ' . Html::openElement(
'span', [
'class' =>
'mw-changeslist-links' ] );
505 foreach ( $tools as $tool ) {
506 $s2 .= Html::rawElement(
'span', [], $tool );
508 $s2 .= Html::closeElement(
'span' );
517 $classes = array_merge( $classes, $newClasses );
518 if ( $tagSummary !==
'' ) {
519 $s2 .=
" $tagSummary";
522 # Include separator between character difference and following text
524 $s .=
' <span class="mw-changeslist-separator"></span> ' . $s2;
527 $attribs = [
'data-mw-revid' => $revRecord->getId() ];
529 $this->getHookRunner()->onPageHistoryLineEnding( $this, $row,
$s, $classes, $attribs );
530 $attribs = array_filter( $attribs,
531 [ Sanitizer::class,
'isReservedDataAttribute' ],
536 $attribs[
'class'] = implode(
' ', $classes );
539 return Xml::tags(
'li', $attribs,
$s ) .
"\n";
560 $cur = $this->historyPage->message[
'cur'];
562 if ( $latest === $rev->
getId()
563 || !$rev->
userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() )
573 'oldid' => $rev->
getId()
589 $last = $this->historyPage->message[
'last'];
591 if ( $next ===
null ) {
592 # Probably no next row
597 if ( $next ===
'unknown' ) {
598 # Next row probably exists but is unknown, use an oldid=prev link
604 'diff' => $prevRev->
getId(),
610 $nextRev = $this->revisionStore->newRevisionFromRow(
612 RevisionStore::READ_NORMAL,
616 if ( !$prevRev->
userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ||
617 !$nextRev->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() )
627 'diff' => $prevRev->
getId(),
628 'oldid' => $next->rev_id
644 $radio = [
'type' =>
'radio',
'value' => $id ];
646 if ( $firstInList ) {
647 $first = Xml::element(
'input',
648 array_merge( $radio, [
649 'style' =>
'visibility:hidden',
651 'id' =>
'mw-oldid-null' ] )
653 $checkmark = [
'checked' =>
'checked' ];
655 # Check visibility of old revisions
656 if ( !$rev->
userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
657 $radio[
'disabled'] =
'disabled';
659 } elseif ( !$this->oldIdChecked ) {
660 $checkmark = [
'checked' =>
'checked' ];
661 $this->oldIdChecked = $id;
665 $first = Xml::element(
'input',
666 array_merge( $radio, $checkmark, [
668 'id' =>
"mw-oldid-$id" ] ) );
671 $second = Xml::element(
'input',
672 array_merge( $radio, $checkmark, [
674 'id' =>
"mw-diff-$id" ] ) );
676 return $first . $second;
691 return parent::isNavigationBarShown();
698 parent::getDefaultQuery();
699 unset( $this->mDefaultQuery[
'date-range-to'] );
700 return $this->mDefaultQuery;
716 return $this->preventClickjacking;
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 generateRollback( $rev, IContextSource $context=null, $options=[ 'verify'])
Generate a rollback link for a given revision.
static revDeleteLinkDisabled( $delete=true)
Creates a dead (show/hide) link for deleting revisions/log entries.
static revComment( $rev, $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 formatRevisionSize( $size)
static revUserTools( $rev, $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