135 $this->tagFilter = $tagFilter;
136 $this->tagInvert = $tagInvert;
139 $this->showTagEditUI = ChangeTags::showTagEditingUI( $this->
getAuthority() );
142 $this->revisionStore = $services->getRevisionStore();
143 $this->linkBatchFactory = $linkBatchFactory ?? $services->getLinkBatchFactory();
144 $this->watchlistManager = $watchlistManager
145 ?? $services->getWatchlistManager();
146 $this->commentFormatter = $commentFormatter ?? $services->getCommentFormatter();
147 $this->hookRunner =
new HookRunner( $hookContainer ?? $services->getHookContainer() );
149 ? $this->watchlistManager->getTitleNotificationTimestamp( $this->
getUser(), $this->
getTitle() )
151 $this->changeTagsStore = $changeTagsStore ?? $services->getChangeTagsStore();
196 if ( !$this->hookRunner->onPageHistoryPager__doBatchLookups( $this, $this->mResult ) ) {
200 # Do a link batch query
201 $batch = $this->linkBatchFactory->newLinkBatch()
202 ->setCaller( __METHOD__ );
205 foreach ( $this->mResult as $row ) {
206 if ( $row->rev_parent_id ) {
207 $revIds[] = (int)$row->rev_parent_id;
209 if ( $row->user_name !==
null ) {
211 }
else { #
for anons or usernames of imported revisions
212 $batch->add(
NS_USER, $row->rev_user_text );
215 $this->revisions[] = $this->revisionStore->newRevisionFromRow(
217 IDBAccessObject::READ_NORMAL,
221 $this->parentLens = $this->revisionStore->getRevisionSizes( $revIds );
224 # The keys of $this->formattedComments will be the same as the keys of $this->revisions
225 $this->formattedComments = $this->commentFormatter->createRevisionBatch()
226 ->revisions( $this->revisions )
229 ->hideIfDeleted(
true )
230 ->useParentheses(
false )
233 $this->mResult->seek( 0 );
364 $resultOffset = $this->getResultOffset();
365 $numRows = min( $this->mResult->numRows(), $this->mLimit );
367 $firstInList = $resultOffset === ( $this->mIsBackwards ? $numRows - 1 : 0 );
369 $nextResultOffset = $resultOffset + ( $this->mIsBackwards ? -1 : 1 );
371 $revRecord = $this->revisions[$resultOffset];
373 $previousRevRecord = $this->revisions[$nextResultOffset] ??
null;
375 $latest = $revRecord->getId() === $this->getWikiPage()->getLatest();
376 $curlink = $this->curLink( $revRecord );
377 if ( $previousRevRecord ) {
379 $lastlink = $this->lastLink( $revRecord, $previousRevRecord );
380 } elseif ( $this->mIsBackwards && $this->mOffset !==
'' ) {
386 $lastlink = $this->lastLink( $revRecord,
null );
390 'class' =>
'mw-history-histlinks-previous',
391 ], $this->historyPage->message[
'last'] );
393 $curLastlinks = Html::rawElement(
'span', [], $curlink ) .
394 Html::rawElement(
'span', [], $lastlink );
395 $histLinks = Html::rawElement(
397 [
'class' =>
'mw-history-histlinks mw-changeslist-links' ],
401 $diffButtons = $this->diffButtons( $revRecord, $firstInList );
402 $s = $histLinks . $diffButtons;
404 $link = $this->revLink( $revRecord );
408 $canRevDelete = $this->getAuthority()->isAllowed(
'deleterevision' );
411 if ( $canRevDelete || $this->showTagEditUI ) {
412 $this->preventClickjacking =
true;
415 if ( !$this->showTagEditUI
416 && !$revRecord->userCan( RevisionRecord::DELETED_RESTRICTED, $this->getAuthority() )
418 $del = Html::check(
'deleterevisions',
false, [
'disabled' =>
'disabled' ] );
422 'ids[' . $revRecord->getId() .
']',
false,
423 [
'form' =>
'mw-history-revisionactions' ]
427 } elseif ( $revRecord->getVisibility() && $this->getAuthority()->isAllowed(
'deletedhistory' ) ) {
429 if ( !$revRecord->userCan( RevisionRecord::DELETED_RESTRICTED, $this->getAuthority() ) ) {
430 $del = Linker::revDeleteLinkDisabled(
false );
434 'type' =>
'revision',
435 'target' => $this->
getTitle()->getPrefixedDBkey(),
436 'ids' => $revRecord->getId()
438 $del .= Linker::revDeleteLink(
440 $revRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ),
449 $lang = $this->getLanguage();
450 $s .=
' ' . Html::rawElement(
'bdi', [
'dir' => $lang->getDir() ], $link );
451 $s .=
" <span class='history-user'>" .
452 Linker::revUserTools( $revRecord,
true,
false ) .
"</span>";
454 if ( $revRecord->isMinor() ) {
455 $s .=
' ' . ChangesList::flag(
'minor', $this->getContext() );
458 # Sometimes rev_len isn't populated
459 if ( $revRecord->getSize() !==
null ) {
460 # Size is always public data
461 $prevSize = $this->parentLens[$row->rev_parent_id] ?? 0;
462 $sDiff = ChangesList::showCharacterDifference( $prevSize, $revRecord->getSize() );
463 $fSize = Linker::formatRevisionSize( $revRecord->getSize() );
464 $s .=
' <span class="mw-changeslist-separator"></span> ' .
"$fSize $sDiff";
467 # Include separator between character difference and following text
468 $s .=
' <span class="mw-changeslist-separator"></span> ';
470 # Text following the character difference is added just before running hooks
471 $comment = $this->formattedComments[$resultOffset];
473 if ( $comment ===
'' ) {
474 $defaultComment = $this->historyPage->message[
'changeslist-nocomment'];
475 $comment =
"<span class=\"comment mw-comment-none\">$defaultComment</span>";
479 if ( $this->notificationTimestamp && $row->rev_timestamp >= $this->notificationTimestamp ) {
480 $s .=
' <span class="updatedmarker">' . $this->historyPage->message[
'updatedmarker'] .
'</span>';
481 $classes[] =
'mw-history-line-updated';
487 $latest && $previousRevRecord,
491 $this->getLinkRenderer()
493 if ( $pagerTools->shouldPreventClickjacking() ) {
494 $this->preventClickjacking =
true;
496 $s .= $pagerTools->toHTML();
499 [ $tagSummary, $newClasses ] = $this->tagsCache->getWithSetCallback(
500 $this->tagsCache->makeKey(
502 $this->getUser()->getName(),
505 fn () => ChangeTags::formatSummaryRow(
511 $classes = array_merge( $classes, $newClasses );
512 if ( $tagSummary !==
'' ) {
513 $s .=
" $tagSummary";
516 $attribs = [
'data-mw-revid' => $revRecord->getId() ];
518 $this->hookRunner->onPageHistoryLineEnding( $this, $row, $s, $classes, $attribs );
519 $attribs = array_filter( $attribs,
520 [ Sanitizer::class,
'isReservedDataAttribute' ],
523 $attribs[
'class'] = $classes;
525 return Html::rawElement(
'li', $attribs, $s ) .
"\n";