MediaWiki REL1_35
ChangesList.php
Go to the documentation of this file.
1<?php
25use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
31use OOUI\IconWidget;
33
35 use ProtectedHookAccessorTrait;
36
37 public const CSS_CLASS_PREFIX = 'mw-changeslist-';
38
42 public $skin;
43
44 protected $watchlist = false;
45 protected $lastdate;
46 protected $message;
47 protected $rc_cache;
48 protected $rcCacheIndex;
49 protected $rclistOpen;
50 protected $rcMoveIndex;
51
54
56 protected $watchMsgCache;
57
61 protected $linkRenderer;
62
66 protected $filterGroups;
67
72 public function __construct( $obj, array $filterGroups = [] ) {
73 if ( $obj instanceof IContextSource ) {
74 $this->setContext( $obj );
75 $this->skin = $obj->getSkin();
76 } else {
77 $this->setContext( $obj->getContext() );
78 $this->skin = $obj;
79 }
80 $this->preCacheMessages();
81 $this->watchMsgCache = new MapCacheLRU( 50 );
82 $this->linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
83 $this->filterGroups = $filterGroups;
84 }
85
94 public static function newFromContext( IContextSource $context, array $groups = [] ) {
95 $user = $context->getUser();
96 $sk = $context->getSkin();
97 $list = null;
98 if ( Hooks::runner()->onFetchChangesList( $user, $sk, $list, $groups ) ) {
99 $new = $context->getRequest()->getBool( 'enhanced', $user->getOption( 'usenewrc' ) );
100
101 return $new ?
102 new EnhancedChangesList( $context, $groups ) :
103 new OldChangesList( $context, $groups );
104 } else {
105 return $list;
106 }
107 }
108
120 public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) {
121 throw new RuntimeException( 'recentChangesLine should be implemented' );
122 }
123
130 protected function getHighlightsContainerDiv() {
131 $highlightColorDivs = '';
132 foreach ( [ 'none', 'c1', 'c2', 'c3', 'c4', 'c5' ] as $color ) {
133 $highlightColorDivs .= Html::rawElement(
134 'div',
135 [
136 'class' => 'mw-rcfilters-ui-highlights-color-' . $color,
137 'data-color' => $color
138 ]
139 );
140 }
141
142 return Html::rawElement(
143 'div',
144 [ 'class' => 'mw-rcfilters-ui-highlights' ],
145 $highlightColorDivs
146 );
147 }
148
153 public function setWatchlistDivs( $value = true ) {
154 $this->watchlist = $value;
155 }
156
161 public function isWatchlist() {
162 return (bool)$this->watchlist;
163 }
164
169 private function preCacheMessages() {
170 if ( !isset( $this->message ) ) {
171 $this->message = [];
172 foreach ( [
173 'cur', 'diff', 'hist', 'enhancedrc-history', 'last', 'blocklink', 'history',
174 'semicolon-separator', 'pipe-separator' ] as $msg
175 ) {
176 $this->message[$msg] = $this->msg( $msg )->escaped();
177 }
178 }
179 }
180
187 public function recentChangesFlags( $flags, $nothing = "\u{00A0}" ) {
188 $f = '';
189 foreach ( array_keys( $this->getConfig()->get( 'RecentChangesFlags' ) ) as $flag ) {
190 $f .= isset( $flags[$flag] ) && $flags[$flag]
191 ? self::flag( $flag, $this->getContext() )
192 : $nothing;
193 }
194
195 return $f;
196 }
197
206 protected function getHTMLClasses( $rc, $watched ) {
207 $classes = [ self::CSS_CLASS_PREFIX . 'line' ];
208 $logType = $rc->mAttribs['rc_log_type'];
209
210 if ( $logType ) {
211 $classes[] = self::CSS_CLASS_PREFIX . 'log';
212 $classes[] = Sanitizer::escapeClass( self::CSS_CLASS_PREFIX . 'log-' . $logType );
213 } else {
214 $classes[] = self::CSS_CLASS_PREFIX . 'edit';
215 $classes[] = Sanitizer::escapeClass( self::CSS_CLASS_PREFIX . 'ns' .
216 $rc->mAttribs['rc_namespace'] . '-' . $rc->mAttribs['rc_title'] );
217 }
218
219 // Indicate watched status on the line to allow for more
220 // comprehensive styling.
221 $classes[] = $watched && $rc->mAttribs['rc_timestamp'] >= $watched
222 ? self::CSS_CLASS_PREFIX . 'line-watched'
223 : self::CSS_CLASS_PREFIX . 'line-not-watched';
224
225 $classes = array_merge( $classes, $this->getHTMLClassesForFilters( $rc ) );
226
227 return $classes;
228 }
229
237 protected function getHTMLClassesForFilters( $rc ) {
238 $classes = [];
239
240 $classes[] = Sanitizer::escapeClass( self::CSS_CLASS_PREFIX . 'ns-' .
241 $rc->mAttribs['rc_namespace'] );
242
243 $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
244 $classes[] = Sanitizer::escapeClass(
245 self::CSS_CLASS_PREFIX .
246 'ns-' .
247 ( $nsInfo->isTalk( $rc->mAttribs['rc_namespace'] ) ? 'talk' : 'subject' )
248 );
249
250 if ( $this->filterGroups !== null ) {
251 foreach ( $this->filterGroups as $filterGroup ) {
252 foreach ( $filterGroup->getFilters() as $filter ) {
253 $filter->applyCssClassIfNeeded( $this, $rc, $classes );
254 }
255 }
256 }
257
258 return $classes;
259 }
260
269 public static function flag( $flag, IContextSource $context = null ) {
270 static $map = [ 'minoredit' => 'minor', 'botedit' => 'bot' ];
271 static $flagInfos = null;
272
273 if ( $flagInfos === null ) {
275 $flagInfos = [];
276 foreach ( $wgRecentChangesFlags as $key => $value ) {
277 $flagInfos[$key]['letter'] = $value['letter'];
278 $flagInfos[$key]['title'] = $value['title'];
279 // Allow customized class name, fall back to flag name
280 $flagInfos[$key]['class'] = $value['class'] ?? $key;
281 }
282 }
283
284 $context = $context ?: RequestContext::getMain();
285
286 // Inconsistent naming, kepted for b/c
287 if ( isset( $map[$flag] ) ) {
288 $flag = $map[$flag];
289 }
290
291 $info = $flagInfos[$flag];
292 return Html::element( 'abbr', [
293 'class' => $info['class'],
294 'title' => wfMessage( $info['title'] )->setContext( $context )->text(),
295 ], wfMessage( $info['letter'] )->setContext( $context )->text() );
296 }
297
302 public function beginRecentChangesList() {
303 $this->rc_cache = [];
304 $this->rcMoveIndex = 0;
305 $this->rcCacheIndex = 0;
306 $this->lastdate = '';
307 $this->rclistOpen = false;
308 $this->getOutput()->addModuleStyles( [
309 'mediawiki.interface.helpers.styles',
310 'mediawiki.special.changeslist'
311 ] );
312
313 return '<div class="mw-changeslist">';
314 }
315
319 public function initChangesListRows( $rows ) {
320 $this->getHookRunner()->onChangesListInitRows( $this, $rows );
321 }
322
333 public static function showCharacterDifference( $old, $new, IContextSource $context = null ) {
334 if ( !$context ) {
335 $context = RequestContext::getMain();
336 }
337
338 $new = (int)$new;
339 $old = (int)$old;
340 $szdiff = $new - $old;
341
343 $config = $context->getConfig();
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';
349 }
350
351 $formattedSize = $lang->formatNum( $szdiff );
352
353 if ( !$fastCharDiff[$code] ) {
354 $formattedSize = $context->msg( 'rc-change-size', $formattedSize )->text();
355 }
356
357 if ( abs( $szdiff ) > abs( $config->get( 'RCChangedSizeThreshold' ) ) ) {
358 $tag = 'strong';
359 } else {
360 $tag = 'span';
361 }
362
363 if ( $szdiff === 0 ) {
364 $formattedSizeClass = 'mw-plusminus-null';
365 } elseif ( $szdiff > 0 ) {
366 $formattedSize = '+' . $formattedSize;
367 $formattedSizeClass = 'mw-plusminus-pos';
368 } else {
369 $formattedSizeClass = 'mw-plusminus-neg';
370 }
371 $formattedSizeClass .= ' mw-diff-bytes';
372
373 $formattedTotalSize = $context->msg( 'rc-change-size-new' )->numParams( $new )->text();
374
375 return Html::element( $tag,
376 [ 'dir' => 'ltr', 'class' => $formattedSizeClass, 'title' => $formattedTotalSize ],
377 $formattedSize ) . $lang->getDirMark();
378 }
379
387 public function formatCharacterDifference( RecentChange $old, RecentChange $new = null ) {
388 $oldlen = $old->mAttribs['rc_old_len'];
389
390 if ( $new ) {
391 $newlen = $new->mAttribs['rc_new_len'];
392 } else {
393 $newlen = $old->mAttribs['rc_new_len'];
394 }
395
396 if ( $oldlen === null || $newlen === null ) {
397 return '';
398 }
399
400 return self::showCharacterDifference( $oldlen, $newlen, $this->getContext() );
401 }
402
407 public function endRecentChangesList() {
408 $out = $this->rclistOpen ? "</ul>\n" : '';
409 $out .= '</div>';
410
411 return $out;
412 }
413
425 public static function revDateLink(
426 RevisionRecord $rev,
427 User $user,
429 $title = null
430 ) {
431 $ts = $rev->getTimestamp();
432 $date = $lang->userTimeAndDate( $ts, $user );
433 if ( RevisionRecord::userCanBitfield(
434 $rev->getVisibility(),
435 RevisionRecord::DELETED_TEXT,
436 $user
437 ) ) {
438 $link = MediaWikiServices::getInstance()->getLinkRenderer()->makeKnownLink(
439 $title ?? $rev->getPageAsLinkTarget(),
440 $date,
441 [ 'class' => 'mw-changeslist-date' ],
442 [ 'oldid' => $rev->getId() ]
443 );
444 } else {
445 $link = htmlspecialchars( $date );
446 }
447 if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
448 $link = "<span class=\"history-deleted mw-changeslist-date\">$link</span>";
449 }
450 return $link;
451 }
452
457 public function insertDateHeader( &$s, $rc_timestamp ) {
458 # Make date header if necessary
459 $date = $this->getLanguage()->userDate( $rc_timestamp, $this->getUser() );
460 if ( $date != $this->lastdate ) {
461 if ( $this->lastdate != '' ) {
462 $s .= "</ul>\n";
463 }
464 $s .= Xml::element( 'h4', null, $date ) . "\n<ul class=\"special\">";
465 $this->lastdate = $date;
466 $this->rclistOpen = true;
467 }
468 }
469
476 public function insertLog( &$s, $title, $logtype, $useParentheses = 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'
481 ] );
482 if ( $useParentheses ) {
483 $s .= $this->msg( 'parentheses' )->rawParams(
484 $link
485 )->escaped();
486 } else {
487 $s .= $link;
488 }
489 }
490
496 public function insertDiffHist( &$s, &$rc, $unpatrolled = null ) {
497 # Diff link
498 if (
499 $rc->mAttribs['rc_type'] == RC_NEW ||
500 $rc->mAttribs['rc_type'] == RC_LOG ||
501 $rc->mAttribs['rc_type'] == RC_CATEGORIZE
502 ) {
503 $diffLink = $this->message['diff'];
504 } elseif ( !self::userCan( $rc, RevisionRecord::DELETED_TEXT, $this->getUser() ) ) {
505 $diffLink = $this->message['diff'];
506 } else {
507 $query = [
508 'curid' => $rc->mAttribs['rc_cur_id'],
509 'diff' => $rc->mAttribs['rc_this_oldid'],
510 'oldid' => $rc->mAttribs['rc_last_oldid']
511 ];
512
513 $diffLink = $this->linkRenderer->makeKnownLink(
514 $rc->getTitle(),
515 new HtmlArmor( $this->message['diff'] ),
516 [ 'class' => 'mw-changeslist-diff' ],
517 $query
518 );
519 }
520 if ( $rc->mAttribs['rc_type'] == RC_CATEGORIZE ) {
521 $histLink = $this->message['hist'];
522 } else {
523 $histLink = $this->linkRenderer->makeKnownLink(
524 $rc->getTitle(),
525 new HtmlArmor( $this->message['hist'] ),
526 [ 'class' => 'mw-changeslist-history' ],
527 [
528 'curid' => $rc->mAttribs['rc_cur_id'],
529 'action' => 'history'
530 ]
531 );
532 }
533
534 $s .= Html::rawElement( 'div', [ 'class' => 'mw-changeslist-links' ],
535 Html::rawElement( 'span', [], $diffLink ) .
536 Html::rawElement( 'span', [], $histLink )
537 ) .
538 ' <span class="mw-changeslist-separator"></span> ';
539 }
540
551 public function getArticleLink( &$rc, $unpatrolled, $watched ) {
552 $params = [];
553 if ( $rc->getTitle()->isRedirect() ) {
554 $params = [ 'redirect' => 'no' ];
555 }
556
557 $articlelink = $this->linkRenderer->makeLink(
558 $rc->getTitle(),
559 null,
560 [ 'class' => 'mw-changeslist-title' ],
561 $params
562 );
563 if ( $this->isDeleted( $rc, RevisionRecord::DELETED_TEXT ) ) {
564 $articlelink = '<span class="history-deleted">' . $articlelink . '</span>';
565 }
566 # To allow for boldening pages watched by this user
567 $articlelink = "<span class=\"mw-title\">{$articlelink}</span>";
568 # RTL/LTR marker
569 $articlelink .= $this->getLanguage()->getDirMark();
570
571 # TODO: Deprecate the $s argument, it seems happily unused.
572 $s = '';
573 $this->getHookRunner()->onChangesListInsertArticleLink( $this, $articlelink,
574 $s, $rc, $unpatrolled, $watched );
575
576 // Watchlist expiry icon.
577 $watchlistExpiry = '';
578 if ( isset( $rc->watchlistExpiry ) && $rc->watchlistExpiry ) {
579 $watchlistExpiry = $this->getWatchlistExpiry( $rc );
580 }
581
582 return "{$s} {$articlelink}{$watchlistExpiry}";
583 }
584
591 public function getWatchlistExpiry( RecentChange $recentChange ): string {
592 $item = WatchedItem::newFromRecentChange( $recentChange, $this->getUser() );
593 // Guard against expired items, even though they shouldn't come here.
594 if ( $item->isExpired() ) {
595 return '';
596 }
597 $daysLeftText = $item->getExpiryInDaysText( $this->getContext() );
598 // Matching widget is also created in ChangesListSpecialPage, for the legend.
599 $widget = new IconWidget( [
600 'icon' => 'clock',
601 'title' => $daysLeftText,
602 'classes' => [ 'mw-changesList-watchlistExpiry' ],
603 ] );
604 $widget->setAttributes( [
605 // Add labels for assistive technologies.
606 'role' => 'img',
607 'aria-label' => $this->msg( 'watchlist-expires-in-aria-label' )->text(),
608 // Days-left is used in resources/src/mediawiki.special.changeslist.watchlistexpiry/watchlistexpiry.js
609 'data-days-left' => $item->getExpiryInDays(),
610 ] );
611 // Add spaces around the widget (the page title is to one side,
612 // and a semicolon or opening-parenthesis to the other).
613 return " $widget ";
614 }
615
624 public function getTimestamp( $rc ) {
625 // This uses the semi-colon separator unless there's a watchlist expiry date for the entry,
626 // because in that case the timestamp is preceeded by a clock icon.
627 // A space is important after mw-changeslist-separator--semicolon to make sure
628 // that whatever comes before it is distinguishable.
629 // (Otherwise your have the text of titles pushing up against the timestamp)
630 // A specific element is used for this purpose as `mw-changeslist-date` is used in a variety
631 // of other places with a different position and the information proceeding getTimestamp can vary.
632 $separatorClass = $rc->watchlistExpiry ? 'mw-changeslist-separator' : 'mw-changeslist-separator--semicolon';
633 return Html::element( 'span', [ 'class' => $separatorClass ] ) . ' ' .
634 '<span class="mw-changeslist-date">' .
635 htmlspecialchars( $this->getLanguage()->userTime(
636 $rc->mAttribs['rc_timestamp'],
637 $this->getUser()
638 ) ) . '</span> <span class="mw-changeslist-separator"></span> ';
639 }
640
647 public function insertTimestamp( &$s, $rc ) {
648 $s .= $this->getTimestamp( $rc );
649 }
650
657 public function insertUserRelatedLinks( &$s, &$rc ) {
658 if ( $this->isDeleted( $rc, RevisionRecord::DELETED_USER ) ) {
659 $s .= ' <span class="history-deleted">' .
660 $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
661 } else {
662 $s .= $this->getLanguage()->getDirMark() . Linker::userLink( $rc->mAttribs['rc_user'],
663 $rc->mAttribs['rc_user_text'] );
665 $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'],
666 false, 0, null,
667 // The text content of tools is not wrapped with parenthesises or "piped".
668 // This will be handled in CSS (T205581).
669 false
670 );
671 }
672 }
673
680 public function insertLogEntry( $rc ) {
681 $formatter = LogFormatter::newFromRow( $rc->mAttribs );
682 $formatter->setContext( $this->getContext() );
683 $formatter->setShowUserToolLinks( true );
684 $mark = $this->getLanguage()->getDirMark();
685
686 return Html::openElement( 'span', [ 'class' => 'mw-changeslist-log-entry' ] )
687 . $formatter->getActionText() . " $mark" . $formatter->getComment()
688 . Html::closeElement( 'span' );
689 }
690
696 public function insertComment( $rc ) {
697 if ( $this->isDeleted( $rc, RevisionRecord::DELETED_COMMENT ) ) {
698 return ' <span class="history-deleted comment">' .
699 $this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
700 } else {
701 return Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle(),
702 // Whether section links should refer to local page (using default false)
703 false,
704 // wikid to generate links for (using default null) */
705 null,
706 // whether parentheses should be rendered as part of the message
707 false );
708 }
709 }
710
716 protected function numberofWatchingusers( $count ) {
717 if ( $count <= 0 ) {
718 return '';
719 }
720
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();
726 }
727 );
728 }
729
736 public static function isDeleted( $rc, $field ) {
737 return ( $rc->mAttribs['rc_deleted'] & $field ) == $field;
738 }
739
749 public static function userCan( $rc, $field, User $user = null ) {
750 if ( $user === null ) {
751 $user = RequestContext::getMain()->getUser();
752 }
753
754 if ( $rc->mAttribs['rc_type'] == RC_LOG ) {
755 return LogEventsList::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
756 }
757
758 return RevisionRecord::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
759 }
760
766 protected function maybeWatchedLink( $link, $watched = false ) {
767 if ( $watched ) {
768 return '<strong class="mw-watched">' . $link . '</strong>';
769 } else {
770 return '<span class="mw-rc-unwatched">' . $link . '</span>';
771 }
772 }
773
780 public function insertRollback( &$s, &$rc ) {
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']
785 ) {
786 $title = $rc->getTitle();
790 if ( MediaWikiServices::getInstance()->getPermissionManager()
791 ->quickUserCan( 'rollback', $this->getUser(), $title )
792 ) {
793 $revRecord = new MutableRevisionRecord( $title );
794 $revRecord->setId( (int)$rc->mAttribs['rc_this_oldid'] );
795 $revRecord->setVisibility( (int)$rc->mAttribs['rc_deleted'] );
796 $user = new UserIdentityValue(
797 (int)$rc->mAttribs['rc_user'],
798 $rc->mAttribs['rc_user_text'],
799 (int)( $rc->mAttribs['rc_actor'] ?? 0 )
800 );
801 $revRecord->setUser( $user );
802
803 $s .= ' ';
805 $revRecord,
806 $this->getContext(),
807 [ 'noBrackets' ]
808 );
809 }
810 }
811 }
812
818 public function getRollback( RecentChange $rc ) {
819 $s = '';
820 $this->insertRollback( $s, $rc );
821 return $s;
822 }
823
829 public function insertTags( &$s, &$rc, &$classes ) {
830 if ( empty( $rc->mAttribs['ts_tags'] ) ) {
831 return;
832 }
833
834 list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow(
835 $rc->mAttribs['ts_tags'],
836 'changeslist',
837 $this->getContext()
838 );
839 $classes = array_merge( $classes, $newClasses );
840 $s .= ' ' . $tagSummary;
841 }
842
849 public function getTags( RecentChange $rc, array &$classes ) {
850 $s = '';
851 $this->insertTags( $s, $rc, $classes );
852 return $s;
853 }
854
855 public function insertExtra( &$s, &$rc, &$classes ) {
856 // Empty, used for subclasses to add anything special.
857 }
858
859 protected function showAsUnpatrolled( RecentChange $rc ) {
860 return self::isUnpatrolled( $rc, $this->getUser() );
861 }
862
868 public static function isUnpatrolled( $rc, User $user ) {
869 if ( $rc instanceof RecentChange ) {
870 $isPatrolled = $rc->mAttribs['rc_patrolled'];
871 $rcType = $rc->mAttribs['rc_type'];
872 $rcLogType = $rc->mAttribs['rc_log_type'];
873 } else {
874 $isPatrolled = $rc->rc_patrolled;
875 $rcType = $rc->rc_type;
876 $rcLogType = $rc->rc_log_type;
877 }
878
879 if ( !$isPatrolled ) {
880 if ( $user->useRCPatrol() ) {
881 return true;
882 }
883 if ( $user->useNPPatrol() && $rcType == RC_NEW ) {
884 return true;
885 }
886 if ( $user->useFilePatrol() && $rcLogType == 'upload' ) {
887 return true;
888 }
889 }
890
891 return false;
892 }
893
903 protected function isCategorizationWithoutRevision( $rcObj ) {
904 return intval( $rcObj->getAttribute( 'rc_type' ) ) === RC_CATEGORIZE
905 && intval( $rcObj->getAttribute( 'rc_this_oldid' ) ) === 0;
906 }
907
913 protected function getDataAttributes( RecentChange $rc ) {
914 $attrs = [];
915
916 $type = $rc->getAttribute( 'rc_source' );
917 switch ( $type ) {
918 case RecentChange::SRC_EDIT:
919 case RecentChange::SRC_NEW:
920 $attrs['data-mw-revid'] = $rc->mAttribs['rc_this_oldid'];
921 break;
922 case RecentChange::SRC_LOG:
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'];
926 break;
927 }
928
929 $attrs[ 'data-mw-ts' ] = $rc->getAttribute( 'rc_timestamp' );
930
931 return $attrs;
932 }
933
941 public function setChangeLinePrefixer( callable $prefixer ) {
942 $this->changeLinePrefixer = $prefixer;
943 }
944}
getPermissionManager()
getUser()
getWatchlistExpiry(WatchedItemStoreInterface $store, Title $title, User $user)
Get existing expiry from the database.
$wgRecentChangesFlags
Flags (letter symbols) shown in recent changes and watchlist to indicate certain types of edits.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
getContext()
static formatSummaryRow( $tags, $page, MessageLocalizer $localizer=null)
Creates HTML for the given tags.
static newFromContext(IContextSource $context, array $groups=[])
Fetch an appropriate changes list class for the specified context Some users might want to use an enh...
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...
maybeWatchedLink( $link, $watched=false)
static userCan( $rc, $field, User $user=null)
Determine if the current user is allowed to view a particular field of this revision,...
setWatchlistDivs( $value=true)
Sets the list to use a "<li class='watchlist-(namespace)-(page)'>" tag.
formatCharacterDifference(RecentChange $old, RecentChange $new=null)
Format the character difference of one or several changes.
insertDateHeader(&$s, $rc_timestamp)
insertRollback(&$s, &$rc)
Insert a rollback link.
showAsUnpatrolled(RecentChange $rc)
static isUnpatrolled( $rc, User $user)
array $filterGroups
getHighlightsContainerDiv()
Get the container for highlights that are used in the new StructuredFilters system.
recentChangesLine(&$rc, $watched=false, $linenumber=null)
Format a line.
recentChangesFlags( $flags, $nothing="\u{00A0}")
Returns the appropriate flags for new page, minor change and patrolling.
getDataAttributes(RecentChange $rc)
Get recommended data attributes for a change line.
numberofWatchingusers( $count)
Returns the string which indicates the number of watching users.
getHTMLClasses( $rc, $watched)
Get an array of default HTML class attributes for the change.
getWatchlistExpiry(RecentChange $recentChange)
Get HTML to display the clock icon for watched items that have a watchlist expiry time.
callable $changeLinePrefixer
getTags(RecentChange $rc, array &$classes)
getArticleLink(&$rc, $unpatrolled, $watched)
Get the HTML link to the changed page, possibly with a prefix from hook handlers, and a suffix for te...
insertUserRelatedLinks(&$s, &$rc)
Insert links to user page, user talk page and eventually a blocking link.
static showCharacterDifference( $old, $new, IContextSource $context=null)
Show formatted char difference.
getRollback(RecentChange $rc)
MapCacheLRU $watchMsgCache
endRecentChangesList()
Returns text for the end of RC.
static flag( $flag, IContextSource $context=null)
Make an "<abbr>" element for a given change flag.
LinkRenderer $linkRenderer
preCacheMessages()
As we use the same small set of messages in various methods and that they are called often,...
insertLogEntry( $rc)
Insert a formatted action.
setChangeLinePrefixer(callable $prefixer)
Sets the callable that generates a change line prefix added to the beginning of each line.
static isDeleted( $rc, $field)
Determine if said field of a revision is hidden.
const CSS_CLASS_PREFIX
insertLog(&$s, $title, $logtype, $useParentheses=true)
insertTags(&$s, &$rc, &$classes)
insertComment( $rc)
Insert a formatted comment.
insertExtra(&$s, &$rc, &$classes)
__construct( $obj, array $filterGroups=[])
initChangesListRows( $rows)
getTimestamp( $rc)
Get the timestamp from $rc formatted with current user's settings and a separator.
isCategorizationWithoutRevision( $rcObj)
Determines whether a revision is linked to this change; this may not be the case when the categorizat...
getHTMLClassesForFilters( $rc)
Get an array of CSS classes attributed to filters for this row.
insertDiffHist(&$s, &$rc, $unpatrolled=null)
insertTimestamp(&$s, $rc)
Insert time timestamp string from $rc into $s.
beginRecentChangesList()
Returns text for the start of the tabular part of RC.
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
IContextSource $context
setContext(IContextSource $context)
Marks HTML that shouldn't be escaped.
Definition HtmlArmor.php:30
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition Language.php:41
static generateRollback( $rev, IContextSource $context=null, $options=[ 'verify'])
Generate a rollback link for a given revision.
Definition Linker.php:1871
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition Linker.php:906
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...
Definition Linker.php:1584
static userToolLinks( $userId, $userText, $redContribsWhenNoEdits=false, $flags=0, $edits=null, $useParentheses=true)
Generate standard user tool links (talk, contributions, block link, etc.)
Definition Linker.php:951
static newFromRow( $row)
Handy shortcut for constructing a formatter directly from database row.
Class to simplify the use of log pages.
Definition LogPage.php:37
Handles a simple LRU key/value map with a maximum number of entries.
Class that generates HTML links for pages.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Page revision base class.
getVisibility()
Get the deletion bitfield of the revision.
getTimestamp()
MCR migration note: this replaces Revision::getTimestamp.
getPageAsLinkTarget()
Returns the title of the page this revision is associated with as a LinkTarget object.
isDeleted( $field)
MCR migration note: this replaces Revision::isDeleted.
Value object representing a user's identity.
Utility class for creating new RC entries.
getAttribute( $name)
Get an attribute value.
The main skin class which provides methods and properties for all other skins.
Definition Skin.php:41
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:60
useFilePatrol()
Check whether to enable new files patrol features for this user.
Definition User.php:3192
useNPPatrol()
Check whether to enable new pages patrol features for this user.
Definition User.php:3180
useRCPatrol()
Check whether to enable recent changes patrol features for this user.
Definition User.php:3171
Representation of a pair of user and title for watchlist entries.
getExpiryInDaysText(MessageLocalizer $msgLocalizer, $isDropdownOption=false)
Get days remaining until a watched item expires as a text.
const RC_NEW
Definition Defines.php:133
const RC_LOG
Definition Defines.php:134
const RC_EDIT
Definition Defines.php:132
const RC_CATEGORIZE
Definition Defines.php:136
Interface for objects which can provide a MediaWiki context on request.
getConfig()
Get the site configuration.
msg( $key,... $params)
This is the method for getting translated interface messages.
Result wrapper for grabbing data queried from an IDatabase object.
if(!isset( $args[0])) $lang