Go to the documentation of this file.
25 use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
35 use ProtectedHookAccessorTrait;
75 $this->skin = $obj->getSkin();
82 $this->linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
98 if (
Hooks::runner()->onFetchChangesList( $user, $sk, $list, $groups ) ) {
121 throw new RuntimeException(
'recentChangesLine should be implemented' );
131 $highlightColorDivs =
'';
132 foreach ( [
'none',
'c1',
'c2',
'c3',
'c4',
'c5' ] as $color ) {
136 'class' =>
'mw-rcfilters-ui-highlights-color-' . $color,
137 'data-color' => $color
144 [
'class' =>
'mw-rcfilters-ui-highlights' ],
154 $this->watchlist = $value;
170 if ( !isset( $this->message ) ) {
173 'cur',
'diff',
'hist',
'enhancedrc-history',
'last',
'blocklink',
'history',
174 'semicolon-separator',
'pipe-separator' ] as $msg
176 $this->message[$msg] = $this->
msg( $msg )->escaped();
189 foreach ( array_keys( $this->
getConfig()->
get(
'RecentChangesFlags' ) ) as $flag ) {
190 $f .= isset( $flags[$flag] ) && $flags[$flag]
207 $classes = [ self::CSS_CLASS_PREFIX .
'line' ];
208 $logType = $rc->mAttribs[
'rc_log_type'];
211 $classes[] = self::CSS_CLASS_PREFIX .
'log';
214 $classes[] = self::CSS_CLASS_PREFIX .
'edit';
216 $rc->mAttribs[
'rc_namespace'] .
'-' . $rc->mAttribs[
'rc_title'] );
221 $classes[] = $watched && $rc->mAttribs[
'rc_timestamp'] >= $watched
222 ? self::CSS_CLASS_PREFIX .
'line-watched'
223 : self::CSS_CLASS_PREFIX .
'line-not-watched';
241 $rc->mAttribs[
'rc_namespace'] );
243 $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
245 self::CSS_CLASS_PREFIX .
247 ( $nsInfo->isTalk( $rc->mAttribs[
'rc_namespace'] ) ?
'talk' :
'subject' )
250 if ( $this->filterGroups !==
null ) {
251 foreach ( $this->filterGroups as $filterGroup ) {
252 foreach ( $filterGroup->getFilters() as $filter ) {
253 $filter->applyCssClassIfNeeded( $this, $rc, $classes );
270 static $map = [
'minoredit' =>
'minor',
'botedit' =>
'bot' ];
271 static $flagInfos =
null;
273 if ( $flagInfos ===
null ) {
277 $flagInfos[$key][
'letter'] = $value[
'letter'];
278 $flagInfos[$key][
'title'] = $value[
'title'];
280 $flagInfos[$key][
'class'] = $value[
'class'] ?? $key;
287 if ( isset( $map[$flag] ) ) {
291 $info = $flagInfos[$flag];
293 'class' => $info[
'class'],
303 $this->rc_cache = [];
304 $this->rcMoveIndex = 0;
305 $this->rcCacheIndex = 0;
306 $this->lastdate =
'';
307 $this->rclistOpen =
false;
309 'mediawiki.interface.helpers.styles',
310 'mediawiki.special.changeslist'
313 return '<div class="mw-changeslist">';
320 $this->getHookRunner()->onChangesListInitRows( $this, $rows );
340 $szdiff = $new - $old;
344 $code =
$lang->getCode();
345 static $fastCharDiff = [];
346 if ( !isset( $fastCharDiff[$code] ) ) {
347 $fastCharDiff[$code] = $config->get(
'MiserMode' )
348 ||
$context->
msg(
'rc-change-size' )->plain() ===
'$1';
351 $formattedSize =
$lang->formatNum( $szdiff );
353 if ( !$fastCharDiff[$code] ) {
354 $formattedSize =
$context->
msg(
'rc-change-size', $formattedSize )->text();
357 if ( abs( $szdiff ) > abs( $config->get(
'RCChangedSizeThreshold' ) ) ) {
363 if ( $szdiff === 0 ) {
364 $formattedSizeClass =
'mw-plusminus-null';
365 } elseif ( $szdiff > 0 ) {
366 $formattedSize =
'+' . $formattedSize;
367 $formattedSizeClass =
'mw-plusminus-pos';
369 $formattedSizeClass =
'mw-plusminus-neg';
371 $formattedSizeClass .=
' mw-diff-bytes';
373 $formattedTotalSize =
$context->
msg(
'rc-change-size-new' )->numParams( $new )->text();
376 [
'dir' =>
'ltr',
'class' => $formattedSizeClass,
'title' => $formattedTotalSize ],
377 $formattedSize ) .
$lang->getDirMark();
388 $oldlen = $old->mAttribs[
'rc_old_len'];
391 $newlen = $new->mAttribs[
'rc_new_len'];
393 $newlen = $old->mAttribs[
'rc_new_len'];
396 if ( $oldlen ===
null || $newlen ===
null ) {
408 $out = $this->rclistOpen ?
"</ul>\n" :
'';
432 $date =
$lang->userTimeAndDate( $ts, $user );
433 if ( RevisionRecord::userCanBitfield(
435 RevisionRecord::DELETED_TEXT,
438 $link = MediaWikiServices::getInstance()->getLinkRenderer()->makeKnownLink(
441 [
'class' =>
'mw-changeslist-date' ],
442 [
'oldid' => $rev->
getId() ]
445 $link = htmlspecialchars( $date );
447 if ( $rev->
isDeleted( RevisionRecord::DELETED_TEXT ) ) {
448 $link =
"<span class=\"history-deleted mw-changeslist-date\">$link</span>";
458 # Make date header if necessary
460 if ( $date != $this->lastdate ) {
461 if ( $this->lastdate !=
'' ) {
464 $s .=
Xml::element(
'h4',
null, $date ) .
"\n<ul class=\"special\">";
465 $this->lastdate = $date;
466 $this->rclistOpen =
true;
477 $page =
new LogPage( $logtype );
478 $logname = $page->getName()->setContext( $this->
getContext() )->text();
479 $link = $this->linkRenderer->makeKnownLink(
$title, $logname, [
480 'class' => $useParentheses ?
'' :
'mw-changeslist-links'
482 if ( $useParentheses ) {
483 $s .= $this->
msg(
'parentheses' )->rawParams(
499 $rc->mAttribs[
'rc_type'] ==
RC_NEW ||
500 $rc->mAttribs[
'rc_type'] ==
RC_LOG ||
503 $diffLink = $this->message[
'diff'];
504 } elseif ( !self::userCan( $rc, RevisionRecord::DELETED_TEXT, $this->
getUser() ) ) {
505 $diffLink = $this->message[
'diff'];
508 'curid' => $rc->mAttribs[
'rc_cur_id'],
509 'diff' => $rc->mAttribs[
'rc_this_oldid'],
510 'oldid' => $rc->mAttribs[
'rc_last_oldid']
513 $diffLink = $this->linkRenderer->makeKnownLink(
516 [
'class' =>
'mw-changeslist-diff' ],
521 $histLink = $this->message[
'hist'];
523 $histLink = $this->linkRenderer->makeKnownLink(
526 [
'class' =>
'mw-changeslist-history' ],
528 'curid' => $rc->mAttribs[
'rc_cur_id'],
529 'action' =>
'history'
538 ' <span class="mw-changeslist-separator"></span> ';
553 if ( $rc->getTitle()->isRedirect() ) {
554 $params = [
'redirect' =>
'no' ];
557 $articlelink = $this->linkRenderer->makeLink(
560 [
'class' =>
'mw-changeslist-title' ],
563 if ( $this->
isDeleted( $rc, RevisionRecord::DELETED_TEXT ) ) {
564 $articlelink =
'<span class="history-deleted">' . $articlelink .
'</span>';
566 # To allow for boldening pages watched by this user
567 $articlelink =
"<span class=\"mw-title\">{$articlelink}</span>";
569 $articlelink .= $this->
getLanguage()->getDirMark();
571 # TODO: Deprecate the $s argument, it seems happily unused.
573 $this->getHookRunner()->onChangesListInsertArticleLink( $this, $articlelink,
574 $s, $rc, $unpatrolled, $watched );
577 $watchlistExpiry =
'';
578 if ( isset( $rc->watchlistExpiry ) && $rc->watchlistExpiry ) {
582 return "{$s} {$articlelink}{$watchlistExpiry}";
594 if ( $item->isExpired() ) {
597 $daysLeftText = $item->getExpiryInDaysText( $this->
getContext() );
599 $widget =
new IconWidget( [
601 'title' => $daysLeftText,
602 'classes' => [
'mw-changesList-watchlistExpiry' ],
604 $widget->setAttributes( [
607 'aria-label' => $this->
msg(
'watchlist-expires-in-aria-label' )->text(),
609 'data-days-left' => $item->getExpiryInDays(),
632 $separatorClass = $rc->watchlistExpiry ?
'mw-changeslist-separator' :
'mw-changeslist-separator--semicolon';
633 return Html::element(
'span', [
'class' => $separatorClass ] ) .
' ' .
634 '<span class="mw-changeslist-date">' .
636 $rc->mAttribs[
'rc_timestamp'],
638 ) ) .
'</span> <span class="mw-changeslist-separator"></span> ';
658 if ( $this->
isDeleted( $rc, RevisionRecord::DELETED_USER ) ) {
659 $s .=
' <span class="history-deleted">' .
660 $this->
msg(
'rev-deleted-user' )->escaped() .
'</span>';
663 $rc->mAttribs[
'rc_user_text'] );
665 $rc->mAttribs[
'rc_user'], $rc->mAttribs[
'rc_user_text'],
682 $formatter->setContext( $this->
getContext() );
683 $formatter->setShowUserToolLinks(
true );
687 . $formatter->getActionText() .
" $mark" . $formatter->getComment()
697 if ( $this->
isDeleted( $rc, RevisionRecord::DELETED_COMMENT ) ) {
698 return ' <span class="history-deleted comment">' .
699 $this->
msg(
'rev-deleted-comment' )->escaped() .
'</span>';
721 return $this->watchMsgCache->getWithSetCallback(
722 "watching-users-msg:$count",
723 function () use ( $count ) {
724 return $this->
msg(
'number-of-watching-users-for-recent-changes' )
725 ->numParams( $count )->escaped();
737 return ( $rc->mAttribs[
'rc_deleted'] & $field ) == $field;
749 public static function userCan( $rc, $field,
User $user =
null ) {
750 if ( $user ===
null ) {
754 if ( $rc->mAttribs[
'rc_type'] ==
RC_LOG ) {
758 return RevisionRecord::userCanBitfield( $rc->mAttribs[
'rc_deleted'], $field, $user );
768 return '<strong class="mw-watched">' . $link .
'</strong>';
770 return '<span class="mw-rc-unwatched">' . $link .
'</span>';
781 if ( $rc->mAttribs[
'rc_type'] ==
RC_EDIT
782 && $rc->mAttribs[
'rc_this_oldid']
783 && $rc->mAttribs[
'rc_cur_id']
784 && $rc->getAttribute(
'page_latest' ) == $rc->mAttribs[
'rc_this_oldid']
794 $revRecord->setId( (
int)$rc->mAttribs[
'rc_this_oldid'] );
795 $revRecord->setVisibility( (
int)$rc->mAttribs[
'rc_deleted'] );
797 (
int)$rc->mAttribs[
'rc_user'],
798 $rc->mAttribs[
'rc_user_text'],
799 (
int)( $rc->mAttribs[
'rc_actor'] ?? 0 )
801 $revRecord->setUser( $user );
830 if ( empty( $rc->mAttribs[
'ts_tags'] ) ) {
835 $rc->mAttribs[
'ts_tags'],
839 $classes = array_merge( $classes, $newClasses );
840 $s .=
' ' . $tagSummary;
870 $isPatrolled = $rc->mAttribs[
'rc_patrolled'];
871 $rcType = $rc->mAttribs[
'rc_type'];
872 $rcLogType = $rc->mAttribs[
'rc_log_type'];
874 $isPatrolled = $rc->rc_patrolled;
875 $rcType = $rc->rc_type;
876 $rcLogType = $rc->rc_log_type;
879 if ( !$isPatrolled ) {
904 return intval( $rcObj->getAttribute(
'rc_type' ) ) ===
RC_CATEGORIZE
905 && intval( $rcObj->getAttribute(
'rc_this_oldid' ) ) === 0;
920 $attrs[
'data-mw-revid'] = $rc->mAttribs[
'rc_this_oldid'];
923 $attrs[
'data-mw-logid'] = $rc->mAttribs[
'rc_logid'];
924 $attrs[
'data-mw-logaction'] =
925 $rc->mAttribs[
'rc_log_type'] .
'/' . $rc->mAttribs[
'rc_log_action'];
929 $attrs[
'data-mw-ts' ] = $rc->
getAttribute(
'rc_timestamp' );
942 $this->changeLinePrefixer = $prefixer;
endRecentChangesList()
Returns text for the end of RC.
getContext()
Get the base IContextSource object.
Marks HTML that shouldn't be escaped.
insertComment( $rc)
Insert a formatted comment.
setWatchlistDivs( $value=true)
Sets the list to use a "<li class='watchlist-(namespace)-(page)'>" tag.
useFilePatrol()
Check whether to enable new files patrol features for this user.
setChangeLinePrefixer(callable $prefixer)
Sets the callable that generates a change line prefix added to the beginning of each line.
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
if(!isset( $args[0])) $lang
Utility class for creating new RC entries.
MapCacheLRU $watchMsgCache
static userToolLinks( $userId, $userText, $redContribsWhenNoEdits=false, $flags=0, $edits=null, $useParentheses=true)
Generate standard user tool links (talk, contributions, block link, etc.)
maybeWatchedLink( $link, $watched=false)
getTags(RecentChange $rc, array &$classes)
getHighlightsContainerDiv()
Get the container for highlights that are used in the new StructuredFilters system.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
static escapeClass( $class)
Given a value, escape it so that it can be used as a CSS class and return it.
useNPPatrol()
Check whether to enable new pages patrol features for this user.
formatCharacterDifference(RecentChange $old, RecentChange $new=null)
Format the character difference of one or several changes.
static userCanBitfield( $bitfield, $field, User $user)
Determine if the current user is allowed to view a particular field of this log row,...
useRCPatrol()
Check whether to enable recent changes patrol features for this user.
getUser()
Stable to override.
static newFromRecentChange(RecentChange $recentChange, UserIdentity $user)
static isDeleted( $rc, $field)
Determine if said field of a revision is hidden.
insertLog(&$s, $title, $logtype, $useParentheses=true)
insertDateHeader(&$s, $rc_timestamp)
static closeElement( $element)
Returns "</$element>".
$wgRecentChangesFlags
Flags (letter symbols) shown in recent changes and watchlist to indicate certain types of edits.
msg( $key,... $params)
This is the method for getting translated interface messages.
static generateRollback( $rev, IContextSource $context=null, $options=[ 'verify'])
Generate a rollback link for a given revision.
insertLogEntry( $rc)
Insert a formatted action.
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
insertTags(&$s, &$rc, &$classes)
Class to simplify the use of log pages.
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Handles a simple LRU key/value map with a maximum number of entries.
static flag( $flag, IContextSource $context=null)
Make an "<abbr>" element for a given change flag.
insertUserRelatedLinks(&$s, &$rc)
Insert links to user page, user talk page and eventually a blocking link.
setContext(IContextSource $context)
getRollback(RecentChange $rc)
getArticleLink(&$rc, $unpatrolled, $watched)
Get the HTML link to the changed page, possibly with a prefix from hook handlers, and a suffix for te...
static newFromContext(IContextSource $context, array $groups=[])
Fetch an appropriate changes list class for the specified context Some users might want to use an enh...
callable $changeLinePrefixer
getDataAttributes(RecentChange $rc)
Get recommended data attributes for a change line.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
static commentBlock( $comment, $title=null, $local=false, $wikiId=null, $useParentheses=true)
Wrap a comment in standard punctuation and formatting if it's non-empty, otherwise return empty strin...
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
insertTimestamp(&$s, $rc)
Insert time timestamp string from $rc into $s.
numberofWatchingusers( $count)
Returns the string which indicates the number of watching users.
showAsUnpatrolled(RecentChange $rc)
static showCharacterDifference( $old, $new, IContextSource $context=null)
Show formatted char difference.
preCacheMessages()
As we use the same small set of messages in various methods and that they are called often,...
static getMain()
Get the RequestContext object associated with the main request.
Interface for objects which can provide a MediaWiki context on request.
getHTMLClasses( $rc, $watched)
Get an array of default HTML class attributes for the change.
__construct( $obj, array $filterGroups=[])
getAttribute( $name)
Get an attribute value.
recentChangesFlags( $flags, $nothing="\u{00A0}")
Returns the appropriate flags for new page, minor change and patrolling.
beginRecentChangesList()
Returns text for the start of the tabular part of RC.
insertDiffHist(&$s, &$rc, $unpatrolled=null)
initChangesListRows( $rows)
getConfig()
Get the site configuration.
isCategorizationWithoutRevision( $rcObj)
Determines whether a revision is linked to this change; this may not be the case when the categorizat...
static openElement( $element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing '/'...
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
getTimestamp( $rc)
Get the timestamp from $rc formatted with current user's settings and a separator.
static isUnpatrolled( $rc, User $user)
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
static revDateLink(RevisionRecord $rev, User $user, Language $lang, $title=null)
Render the date and time of a revision in the current user language based on whether the user is able...
The main skin class which provides methods and properties for all other skins.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
getHTMLClassesForFilters( $rc)
Get an array of CSS classes attributed to filters for this row.
recentChangesLine(&$rc, $watched=false, $linenumber=null)
Format a line.
static userCan( $rc, $field, User $user=null)
Determine if the current user is allowed to view a particular field of this revision,...
insertExtra(&$s, &$rc, &$classes)
insertRollback(&$s, &$rc)
Insert a rollback link.
getWatchlistExpiry(RecentChange $recentChange)
Get HTML to display the clock icon for watched items that have a watchlist expiry time.
LinkRenderer $linkRenderer