147 if ( !$this->getHookRunner()->onPageHistoryPager__doBatchLookups( $this, $this->mResult ) ) {
151 # Do a link batch query
152 $this->mResult->seek( 0 );
155 foreach ( $this->mResult as $row ) {
156 if ( $row->rev_parent_id ) {
157 $revIds[] = $row->rev_parent_id;
159 if ( $row->user_name !==
null ) {
160 $batch->add(
NS_USER, $row->user_name );
162 }
else { #
for anons or usernames of imported revisions
163 $batch->add(
NS_USER, $row->rev_user_text );
167 $this->parentLens = $this->revisionStore->getRevisionSizes( $revIds );
169 $this->mResult->seek( 0 );
186 $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
187 $this->lastRow =
false;
189 $this->oldIdChecked = 0;
194 $this->
getOutput()->wrapWikiMsg(
"<div class='mw-history-legend'>\n$1\n</div>",
'histlegend' );
195 $s = Html::openElement(
'form', [
197 'id' =>
'mw-history-compare'
199 $s .= Html::hidden(
'title', $this->
getTitle()->getPrefixedDBkey() ) .
"\n";
200 $s .= Html::hidden(
'action',
'historysubmit' ) .
"\n";
201 $s .= Html::hidden(
'type',
'revision' ) .
"\n";
203 $this->buttons .= Html::openElement(
204 'div', [
'class' =>
'mw-history-compareselectedversions' ] );
205 $className =
'historysubmit mw-history-compareselectedversions-button mw-ui-button';
206 $attrs = [
'class' => $className ]
208 $this->buttons .= $this->
submitButton( $this->
msg(
'compareselectedversions' )->text(),
214 if ( $permissionManager->userHasRight( $user,
'deleterevision' ) ) {
216 'revisiondelete',
'showhideselectedversions' );
218 if ( $this->showTagEditUI ) {
220 'editchangetags',
'history-edit-tags' );
222 if ( $actionButtons ) {
223 $this->buttons .= Xml::tags(
'div', [
'class' =>
224 'mw-history-revisionactions' ], $actionButtons );
227 if ( $permissionManager->userHasRight( $user,
'deleterevision' ) || $this->showTagEditUI ) {
231 $this->buttons .=
'</div>';
233 $s .= $this->buttons;
234 $s .=
'<ul id="pagehistory">' .
"\n";
322 private function historyLine( $row, $next, $notificationtimestamp =
false,
323 $dummy =
false, $firstInList =
false ) {
324 $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
326 $revRecord = $this->revisionStore->newRevisionFromRow(
328 RevisionStore::READ_NORMAL,
332 if ( is_object( $next ) ) {
333 $previousRevRecord = $this->revisionStore->newRevisionFromRow(
335 RevisionStore::READ_NORMAL,
339 $previousRevRecord =
null;
342 $latest = $revRecord->getId() === $this->
getWikiPage()->getLatest();
343 $curlink = $this->
curLink( $revRecord );
344 $lastlink = $this->
lastLink( $revRecord, $next );
345 $curLastlinks = Html::rawElement(
'span', [], $curlink ) .
346 Html::rawElement(
'span', [], $lastlink );
347 $histLinks = Html::rawElement(
349 [
'class' =>
'mw-history-histlinks mw-changeslist-links' ],
353 $diffButtons = $this->
diffButtons( $revRecord, $firstInList );
354 $s = $histLinks . $diffButtons;
356 $link = $this->
revLink( $revRecord );
361 $canRevDelete = $permissionManager->userHasRight( $user,
'deleterevision' );
364 $visibility = $revRecord->getVisibility();
365 if ( $canRevDelete || $this->showTagEditUI ) {
369 if ( !$this->showTagEditUI
370 && !RevisionRecord::userCanBitfield(
372 RevisionRecord::DELETED_RESTRICTED,
376 $del = Xml::check(
'deleterevisions',
false, [
'disabled' =>
'disabled' ] );
379 $del = Xml::check(
'showhiderevisions',
false,
380 [
'name' =>
'ids[' . $revRecord->getId() .
']' ] );
383 } elseif ( $visibility && $permissionManager->userHasRight( $user,
'deletedhistory' ) ) {
385 if ( !RevisionRecord::userCanBitfield(
387 RevisionRecord::DELETED_RESTRICTED,
394 'type' =>
'revision',
395 'target' => $this->
getTitle()->getPrefixedDBkey(),
396 'ids' => $revRecord->getId()
400 $revRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ),
410 $dirmark =
$lang->getDirMark();
414 $s .=
" <span class='history-user'>" .
418 if ( $revRecord->isMinor() ) {
419 $s .=
' ' . ChangesList::flag(
'minor', $this->
getContext() );
422 # Sometimes rev_len isn't populated
423 if ( $revRecord->getSize() !==
null ) {
424 # Size is always public data
425 $prevSize = $this->parentLens[$row->rev_parent_id] ?? 0;
426 $sDiff = ChangesList::showCharacterDifference( $prevSize, $revRecord->getSize() );
428 $s .=
' <span class="mw-changeslist-separator"></span> ' .
"$fSize $sDiff";
431 # Text following the character difference is added just before running hooks
434 if ( $notificationtimestamp && ( $row->rev_timestamp >= $notificationtimestamp ) ) {
435 $s2 .=
' <span class="updatedmarker">' . $this->
msg(
'updatedmarker' )->escaped() .
'</span>';
436 $classes[] =
'mw-history-line-updated';
441 # Rollback and undo links
443 if ( $previousRevRecord &&
444 $permissionManager->quickUserCan(
'edit', $user, $this->getTitle() )
446 if ( $latest && $permissionManager->quickUserCan(
'rollback',
447 $user, $this->getTitle() )
453 [
'verify',
'noBrackets' ]
455 if ( $rollbackLink ) {
457 $tools[] = $rollbackLink;
461 if ( !$revRecord->isDeleted( RevisionRecord::DELETED_TEXT )
462 && !$previousRevRecord->isDeleted( RevisionRecord::DELETED_TEXT )
464 # Create undo tooltip for the first (=latest) line only
465 $undoTooltip = $latest
466 ? [
'title' => $this->
msg(
'tooltip-undo' )->text() ]
470 $this->
msg(
'editundo' )->text(),
474 'undoafter' => $previousRevRecord->getId(),
475 'undo' => $revRecord->getId()
478 $tools[] =
"<span class=\"mw-history-undo\">{$undolink}</span>";
482 $this->getHookRunner()->onHistoryTools(
490 if ( $this->getHookContainer()->isRegistered(
'HistoryRevisionTools' ) ) {
492 $this->getHookRunner()->onHistoryRevisionTools(
495 $previousRevRecord ?
new Revision( $previousRevRecord ) :
null,
501 $s2 .=
' ' . Html::openElement(
'span', [
'class' =>
'mw-changeslist-links' ] );
502 foreach ( $tools as $tool ) {
503 $s2 .= Html::rawElement(
'span', [], $tool );
505 $s2 .= Html::closeElement(
'span' );
514 $classes = array_merge( $classes, $newClasses );
515 if ( $tagSummary !==
'' ) {
516 $s2 .=
" $tagSummary";
519 # Include separator between character difference and following text
521 $s .=
' <span class="mw-changeslist-separator"></span> ' . $s2;
524 $attribs = [
'data-mw-revid' => $revRecord->getId() ];
526 $this->getHookRunner()->onPageHistoryLineEnding( $this, $row,
$s, $classes, $attribs );
527 $attribs = array_filter( $attribs,
528 [ Sanitizer::class,
'isReservedDataAttribute' ],
533 $attribs[
'class'] = implode(
' ', $classes );
536 return Xml::tags(
'li', $attribs,
$s ) .
"\n";
590 $last = $this->historyPage->message[
'last'];
592 if ( $next ===
null ) {
593 # Probably no next row
598 if ( $next ===
'unknown' ) {
599 # Next row probably exists but is unknown, use an oldid=prev link
605 'diff' => $prevRev->
getId(),
611 $nextRev = $this->revisionStore->newRevisionFromRow(
613 RevisionStore::READ_NORMAL,
617 if ( !RevisionRecord::userCanBitfield(
619 RevisionRecord::DELETED_TEXT,
621 ) || !RevisionRecord::userCanBitfield(
622 $nextRev->getVisibility(),
623 RevisionRecord::DELETED_TEXT,
635 'diff' => $prevRev->
getId(),
636 'oldid' => $next->rev_id
652 $radio = [
'type' =>
'radio',
'value' => $id ];
654 if ( $firstInList ) {
655 $first = Xml::element(
'input',
656 array_merge( $radio, [
657 'style' =>
'visibility:hidden',
659 'id' =>
'mw-oldid-null' ] )
661 $checkmark = [
'checked' =>
'checked' ];
663 # Check visibility of old revisions
664 if ( !RevisionRecord::userCanBitfield(
666 RevisionRecord::DELETED_TEXT,
669 $radio[
'disabled'] =
'disabled';
671 } elseif ( !$this->oldIdChecked ) {
672 $checkmark = [
'checked' =>
'checked' ];
673 $this->oldIdChecked = $id;
677 $first = Xml::element(
'input',
678 array_merge( $radio, $checkmark, [
680 'id' =>
"mw-oldid-$id" ] ) );
683 $second = Xml::element(
'input',
684 array_merge( $radio, $checkmark, [
686 'id' =>
"mw-diff-$id" ] ) );
688 return $first . $second;