128 $this->tagFilter = $tagFilter;
129 $this->tagInvert = $tagInvert;
135 $this->revisionStore = $services->getRevisionStore();
136 $this->linkBatchFactory = $linkBatchFactory ?? $services->getLinkBatchFactory();
137 $this->watchlistManager = $watchlistManager
138 ?? $services->getWatchlistManager();
139 $this->commentFormatter = $commentFormatter ?? $services->getCommentFormatter();
140 $this->hookRunner =
new HookRunner( $hookContainer ?? $services->getHookContainer() );
142 ? $this->watchlistManager->getTitleNotificationTimestamp( $this->
getUser(), $this->
getTitle() )
144 $this->changeTagsStore = $changeTagsStore ?? $services->getChangeTagsStore();
189 if ( !$this->hookRunner->onPageHistoryPager__doBatchLookups( $this, $this->mResult ) ) {
193 # Do a link batch query
194 $batch = $this->linkBatchFactory->newLinkBatch();
197 foreach ( $this->mResult as $row ) {
198 if ( $row->rev_parent_id ) {
199 $revIds[] = (int)$row->rev_parent_id;
201 if ( $row->user_name !==
null ) {
202 $batch->add(
NS_USER, $row->user_name );
204 }
else { #
for anons or usernames of imported revisions
205 $batch->add(
NS_USER, $row->rev_user_text );
208 $this->revisions[] = $this->revisionStore->newRevisionFromRow(
210 RevisionStore::READ_NORMAL,
214 $this->parentLens = $this->revisionStore->getRevisionSizes( $revIds );
217 # The keys of $this->formattedComments will be the same as the keys of $this->revisions
218 $this->formattedComments = $this->commentFormatter->createRevisionBatch()
219 ->revisions( $this->revisions )
222 ->hideIfDeleted(
true )
223 ->useParentheses(
false )
226 $this->mResult->seek( 0 );
243 $this->oldIdChecked = 0;
248 $this->
getOutput()->wrapWikiMsg(
"<div class='mw-history-legend'>\n$1\n</div>",
'histlegend' );
250 $s = Html::openElement(
'form', [
252 'id' =>
'mw-history-compare'
254 $s .= Html::hidden(
'title', $this->
getTitle()->getPrefixedDBkey() ) .
"\n";
256 $this->buttons .= Html::openElement(
257 'div', [
'class' =>
'mw-history-compareselectedversions' ] );
258 $className =
'historysubmit mw-history-compareselectedversions-button cdx-button';
259 $attrs = [
'class' => $className ]
260 + Linker::tooltipAndAccesskeyAttribs(
'compareselectedversions' );
261 $this->buttons .= $this->submitButton( $this->
msg(
'compareselectedversions' )->text(),
266 if ( $this->
getAuthority()->isAllowed(
'deleterevision' ) ) {
267 $actionButtons .= $this->getRevisionButton(
268 'Revisiondelete',
'showhideselectedversions',
'mw-history-revisiondelete-button' );
270 if ( $this->showTagEditUI ) {
271 $actionButtons .= $this->getRevisionButton(
272 'EditTags',
'history-edit-tags',
'mw-history-editchangetags-button' );
274 if ( $actionButtons ) {
281 $s = Html::rawElement(
'form', [
283 'id' =>
'mw-history-revisionactions',
285 $s .= Html::hidden(
'type',
'revision', [
'form' =>
'mw-history-revisionactions' ] ) .
"\n";
287 $this->buttons .= Xml::tags(
'div', [
'class' =>
288 'mw-history-revisionactions' ], $actionButtons );
291 if ( $this->
getAuthority()->isAllowed(
'deleterevision' ) || $this->showTagEditUI ) {
295 $this->buttons .=
'</div>';
300 $s .=
'<section id="pagehistory" class="mw-pager-body">';
358 $numRows = min( $this->mResult->numRows(), $this->mLimit );
360 $firstInList = $resultOffset === ( $this->mIsBackwards ? $numRows - 1 : 0 );
362 $nextResultOffset = $resultOffset + ( $this->mIsBackwards ? -1 : 1 );
364 $revRecord = $this->revisions[$resultOffset];
366 $previousRevRecord = $this->revisions[$nextResultOffset] ??
null;
368 $latest = $revRecord->getId() === $this->
getWikiPage()->getLatest();
369 $curlink = $this->curLink( $revRecord );
370 if ( $previousRevRecord ) {
372 $lastlink = $this->lastLink( $revRecord, $previousRevRecord );
373 } elseif ( $this->mIsBackwards && $this->mOffset !==
'' ) {
379 $lastlink = $this->lastLink( $revRecord,
null );
382 $lastlink = Html::element(
'span', [
383 'class' =>
'mw-history-histlinks-previous',
384 ], $this->historyPage->message[
'last'] );
386 $curLastlinks = Html::rawElement(
'span', [], $curlink ) .
387 Html::rawElement(
'span', [], $lastlink );
388 $histLinks = Html::rawElement(
390 [
'class' =>
'mw-history-histlinks mw-changeslist-links' ],
394 $diffButtons = $this->diffButtons( $revRecord, $firstInList );
395 $s = $histLinks . $diffButtons;
397 $link = $this->revLink( $revRecord );
401 $canRevDelete = $this->
getAuthority()->isAllowed(
'deleterevision' );
404 if ( $canRevDelete || $this->showTagEditUI ) {
405 $this->preventClickjacking =
true;
408 if ( !$this->showTagEditUI
409 && !$revRecord->userCan( RevisionRecord::DELETED_RESTRICTED, $this->getAuthority() )
411 $del = Xml::check(
'deleterevisions',
false, [
'disabled' =>
'disabled' ] );
414 $del = Xml::check(
'showhiderevisions',
false,
415 [
'name' =>
'ids[' . $revRecord->getId() .
']',
'form' =>
'mw-history-revisionactions' ] );
418 } elseif ( $revRecord->getVisibility() && $this->getAuthority()->isAllowed(
'deletedhistory' ) ) {
420 if ( !$revRecord->userCan( RevisionRecord::DELETED_RESTRICTED, $this->getAuthority() ) ) {
421 $del = Linker::revDeleteLinkDisabled(
false );
425 'type' =>
'revision',
426 'target' => $this->
getTitle()->getPrefixedDBkey(),
427 'ids' => $revRecord->getId()
429 $del .= Linker::revDeleteLink(
431 $revRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ),
441 $dirmark = $lang->getDirMark();
445 $s .=
" <span class='history-user'>" .
446 Linker::revUserTools( $revRecord,
true,
false ) .
"</span>";
449 if ( $revRecord->isMinor() ) {
450 $s .=
' ' . ChangesList::flag(
'minor', $this->
getContext() );
453 # Sometimes rev_len isn't populated
454 if ( $revRecord->getSize() !==
null ) {
455 # Size is always public data
456 $prevSize = $this->parentLens[$row->rev_parent_id] ?? 0;
457 $sDiff = ChangesList::showCharacterDifference( $prevSize, $revRecord->getSize() );
458 $fSize = Linker::formatRevisionSize( $revRecord->getSize() );
459 $s .=
' <span class="mw-changeslist-separator"></span> ' .
"$fSize $sDiff";
462 # Include separator between character difference and following text
463 $s .=
' <span class="mw-changeslist-separator"></span> ';
465 # Text following the character difference is added just before running hooks
466 $comment = $this->formattedComments[$resultOffset];
468 if ( $comment ===
'' ) {
469 $defaultComment = $this->historyPage->message[
'changeslist-nocomment'];
470 $comment =
"<span class=\"comment mw-comment-none\">$defaultComment</span>";
474 if ( $this->notificationTimestamp && $row->rev_timestamp >= $this->notificationTimestamp ) {
475 $s .=
' <span class="updatedmarker">' . $this->historyPage->message[
'updatedmarker'] .
'</span>';
476 $classes[] =
'mw-history-line-updated';
482 $latest && $previousRevRecord,
488 if ( $pagerTools->shouldPreventClickjacking() ) {
489 $this->preventClickjacking =
true;
491 $s .= $pagerTools->toHTML();
494 [ $tagSummary, $newClasses ] = $this->tagsCache->getWithSetCallback(
495 $this->tagsCache->makeKey(
497 $this->getUser()->getName(),
506 $classes = array_merge( $classes, $newClasses );
507 if ( $tagSummary !==
'' ) {
508 $s .=
" $tagSummary";
511 $attribs = [
'data-mw-revid' => $revRecord->getId() ];
513 $this->hookRunner->onPageHistoryLineEnding( $this, $row, $s, $classes, $attribs );
514 $attribs = array_filter( $attribs,
515 [ Sanitizer::class,
'isReservedDataAttribute' ],
520 $attribs[
'class'] = implode(
' ', $classes );
523 return Xml::tags(
'li', $attribs, $s ) .
"\n";