MediaWiki master
EnhancedChangesList.php
Go to the documentation of this file.
1<?php
22
23use DomainException;
34
41
46
50 protected $rc_cache;
51
55 protected $templateParser;
56
61 public function __construct( $context, array $filterGroups = [] ) {
62 parent::__construct( $context, $filterGroups );
63
64 // message is set by the parent ChangesList class
65 $this->cacheEntryFactory = new RCCacheEntryFactory(
66 $context,
67 $this->message,
68 $this->linkRenderer,
69 $this->userLinkRenderer
70 );
71 $this->templateParser = new TemplateParser();
72 }
73
78 public function beginRecentChangesList() {
79 $this->getOutput()->addModuleStyles( [
80 'mediawiki.special.changeslist.enhanced',
81 ] );
82
83 parent::beginRecentChangesList();
84 return '<div class="mw-changeslist" aria-live="polite">';
85 }
86
96 public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) {
97 $date = $this->getLanguage()->userDate(
98 $rc->mAttribs['rc_timestamp'],
99 $this->getUser()
100 );
101 if ( $this->lastdate === '' ) {
102 $this->lastdate = $date;
103 }
104
105 $ret = '';
106
107 # If it's a new day, flush the cache and update $this->lastdate
108 if ( $date !== $this->lastdate ) {
109 # Process current cache (uses $this->lastdate to generate a heading)
110 $ret = $this->recentChangesBlock();
111 $this->rc_cache = [];
112 $this->lastdate = $date;
113 }
114
115 $cacheEntry = $this->cacheEntryFactory->newFromRecentChange( $rc, $watched );
116 $this->addCacheEntry( $cacheEntry );
117
118 return $ret;
119 }
120
125 protected function addCacheEntry( RCCacheEntry $cacheEntry ) {
126 $cacheGroupingKey = $this->makeCacheGroupingKey( $cacheEntry );
127 $this->rc_cache[$cacheGroupingKey][] = $cacheEntry;
128 }
129
137 protected function makeCacheGroupingKey( RCCacheEntry $cacheEntry ) {
138 $title = $cacheEntry->getTitle();
139 $cacheGroupingKey = $title->getPrefixedDBkey();
140
141 $type = $cacheEntry->mAttribs['rc_type'];
142
143 if ( $type == RC_LOG ) {
144 // Group by log type
145 $cacheGroupingKey = SpecialPage::getTitleFor(
146 'Log',
147 $cacheEntry->mAttribs['rc_log_type']
148 )->getPrefixedDBkey();
149 }
150
151 return $cacheGroupingKey;
152 }
153
159 protected function recentChangesBlockGroup( $block ) {
160 $recentChangesFlags = $this->getConfig()->get( MainConfigNames::RecentChangesFlags );
161
162 # Add the namespace and title of the block as part of the class
163 $tableClasses = [ 'mw-enhanced-rc', 'mw-changeslist-line' ];
164 if ( $block[0]->mAttribs['rc_log_type'] ) {
165 # Log entry
166 $tableClasses[] = 'mw-changeslist-log';
167 $tableClasses[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
168 . $block[0]->mAttribs['rc_log_type'] );
169 } else {
170 $tableClasses[] = 'mw-changeslist-edit';
171 $tableClasses[] = Sanitizer::escapeClass( 'mw-changeslist-ns'
172 . $block[0]->mAttribs['rc_namespace'] . '-' . $block[0]->mAttribs['rc_title'] );
173 }
174 if ( $block[0]->watched ) {
175 $tableClasses[] = 'mw-changeslist-line-watched';
176 } else {
177 $tableClasses[] = 'mw-changeslist-line-not-watched';
178 }
179
180 # Collate list of users
181 $usercounts = [];
182 $userlinks = [];
183 # Some catalyst variables...
184 $namehidden = true;
185 $allLogs = true;
186 $RCShowChangedSize = $this->getConfig()->get( MainConfigNames::RCShowChangedSize );
187
188 # Default values for RC flags
189 $collectedRcFlags = [];
190 foreach ( $recentChangesFlags as $key => $value ) {
191 $flagGrouping = $value['grouping'] ?? 'any';
192 switch ( $flagGrouping ) {
193 case 'all':
194 $collectedRcFlags[$key] = true;
195 break;
196 case 'any':
197 $collectedRcFlags[$key] = false;
198 break;
199 default:
200 throw new DomainException( "Unknown grouping type \"{$flagGrouping}\"" );
201 }
202 }
203 foreach ( $block as $rcObj ) {
204 // If all log actions to this page were hidden, then don't
205 // give the name of the affected page for this block!
206 if ( !static::isDeleted( $rcObj, LogPage::DELETED_ACTION ) ) {
207 $namehidden = false;
208 }
209 $username = $rcObj->getPerformerIdentity()->getName();
210 $userlink = $rcObj->userlink;
211 if ( !isset( $usercounts[$username] ) ) {
212 $usercounts[$username] = 0;
213 $userlinks[$username] = $userlink;
214 }
215 if ( $rcObj->mAttribs['rc_type'] != RC_LOG ) {
216 $allLogs = false;
217 }
218
219 $usercounts[$username]++;
220 }
221
222 # Sort the list and convert to text
223 krsort( $usercounts );
224 asort( $usercounts );
225 $users = [];
226 foreach ( $usercounts as $username => $count ) {
227 $text = (string)$userlinks[$username];
228 if ( $count > 1 ) {
229 $formattedCount = $this->msg( 'ntimes' )->numParams( $count )->escaped();
230 $text .= ' ' . $this->msg( 'parentheses' )->rawParams( $formattedCount )->escaped();
231 }
232 $users[] = $text;
233 }
234
235 # Article link
236 $articleLink = '';
237 $revDeletedMsg = false;
238 if ( $namehidden ) {
239 $revDeletedMsg = $this->msg( 'rev-deleted-event' )->escaped();
240 } elseif ( $allLogs ) {
241 $articleLink = $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
242 } else {
243 $articleLink = $this->getArticleLink(
244 $block[0], $block[0]->unpatrolled, $block[0]->watched );
245 }
246
247 # Sub-entries
248 $lines = [];
249 $filterClasses = [];
250 foreach ( $block as $i => $rcObj ) {
251 $line = $this->getLineData( $block, $rcObj );
252 if ( !$line ) {
253 // completely ignore this RC entry if we don't want to render it
254 unset( $block[$i] );
255 continue;
256 }
257
258 // Roll up flags
259 foreach ( $line['recentChangesFlagsRaw'] as $key => $value ) {
260 $flagGrouping = ( $recentChangesFlags[$key]['grouping'] ?? 'any' );
261 switch ( $flagGrouping ) {
262 case 'all':
263 if ( !$value ) {
264 $collectedRcFlags[$key] = false;
265 }
266 break;
267 case 'any':
268 if ( $value ) {
269 $collectedRcFlags[$key] = true;
270 }
271 break;
272 default:
273 throw new DomainException( "Unknown grouping type \"{$flagGrouping}\"" );
274 }
275 }
276
277 // Roll up filter-based CSS classes
278 $filterClasses = array_merge( $filterClasses, $this->getHTMLClassesForFilters( $rcObj ) );
279 // Add classes for change tags separately, getHTMLClassesForFilters() doesn't add them
280 $this->getTags( $rcObj, $filterClasses );
281 $filterClasses = array_unique( $filterClasses );
282
283 $lines[] = $line;
284 }
285
286 // Further down are some assumptions that $block is a 0-indexed array
287 // with (count-1) as last key. Let's make sure it is.
288 $block = array_values( $block );
289 $filterClasses = array_values( $filterClasses );
290
291 if ( !$block || !$lines ) {
292 // if we can't show anything, don't display this block altogether
293 return '';
294 }
295
296 $logText = $this->getLogText( $block, [], $allLogs,
297 $collectedRcFlags['newpage'], $namehidden
298 );
299
300 # Character difference (does not apply if only log items)
301 $charDifference = false;
302 if ( $RCShowChangedSize && !$allLogs ) {
303 $last = 0;
304 $first = count( $block ) - 1;
305 # Some events (like logs and category changes) have an "empty" size, so we need to skip those...
306 while ( $last < $first && $block[$last]->mAttribs['rc_new_len'] === null ) {
307 $last++;
308 }
309 while ( $last < $first && $block[$first]->mAttribs['rc_old_len'] === null ) {
310 $first--;
311 }
312 # Get net change
313 $charDifference = $this->formatCharacterDifference( $block[$first], $block[$last] ) ?: false;
314 }
315
316 $numberofWatchingusers = $this->numberofWatchingusers( $block[0]->numberofWatchingusers );
317 $usersList = $this->msg( 'brackets' )->rawParams(
318 implode( $this->message['semicolon-separator'], $users )
319 )->escaped();
320
321 $prefix = '';
322 if ( is_callable( $this->changeLinePrefixer ) ) {
323 $prefix = ( $this->changeLinePrefixer )( $block[0], $this, true );
324 }
325
326 $templateParams = [
327 'checkboxId' => 'mw-checkbox-' . base64_encode( random_bytes( 3 ) ),
328 'articleLink' => $articleLink,
329 'charDifference' => $charDifference,
330 'collectedRcFlags' => $this->recentChangesFlags( $collectedRcFlags ),
331 'filterClasses' => $filterClasses,
332 'lines' => $lines,
333 'logText' => $logText,
334 'numberofWatchingusers' => $numberofWatchingusers,
335 'prefix' => $prefix,
336 'rev-deleted-event' => $revDeletedMsg,
337 'tableClasses' => $tableClasses,
338 'timestamp' => $block[0]->timestamp,
339 'fullTimestamp' => $block[0]->getAttribute( 'rc_timestamp' ),
340 'users' => $usersList,
341 ];
342
343 $this->rcCacheIndex++;
344
345 return $this->templateParser->processTemplate(
346 'EnhancedChangesListGroup',
347 $templateParams
348 );
349 }
350
357 protected function getLineData( array $block, RCCacheEntry $rcObj, array $queryParams = [] ) {
358 $RCShowChangedSize = $this->getConfig()->get( MainConfigNames::RCShowChangedSize );
359
360 $type = $rcObj->mAttribs['rc_type'];
361 $data = [];
362 $lineParams = [ 'targetTitle' => $rcObj->getTitle() ];
363
364 $classes = [ 'mw-enhanced-rc' ];
365 if ( $rcObj->watched ) {
366 $classes[] = 'mw-enhanced-watched';
367 }
368 $classes = array_merge( $classes, $this->getHTMLClasses( $rcObj, $rcObj->watched ) );
369
370 $separator = ' <span class="mw-changeslist-separator"></span> ';
371
372 $data['recentChangesFlags'] = [
373 'newpage' => $type == RC_NEW,
374 'minor' => $rcObj->mAttribs['rc_minor'],
375 'unpatrolled' => $rcObj->unpatrolled,
376 'bot' => $rcObj->mAttribs['rc_bot'],
377 ];
378
379 # Log timestamp
380 if ( $type == RC_LOG ) {
381 $link = htmlspecialchars( $rcObj->timestamp );
382 # Revision link
383 } elseif ( !ChangesList::userCan( $rcObj, RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
384 $link = Html::element( 'span', [ 'class' => 'history-deleted' ], $rcObj->timestamp );
385 } else {
386 $params = [];
387 $params['curid'] = $rcObj->mAttribs['rc_cur_id'];
388 if ( $rcObj->mAttribs['rc_this_oldid'] != 0 ) {
389 $params['oldid'] = $rcObj->mAttribs['rc_this_oldid'];
390 }
391 // FIXME: The link has incorrect "title=" when rc_type = RC_CATEGORIZE.
392 // rc_cur_id refers to the page that was categorized
393 // whereas RecentChange::getTitle refers to the category.
394 $link = $this->linkRenderer->makeKnownLink(
395 $rcObj->getTitle(),
396 $rcObj->timestamp,
397 [],
398 $params + $queryParams
399 );
400 if ( static::isDeleted( $rcObj, RevisionRecord::DELETED_TEXT ) ) {
401 $link = '<span class="history-deleted">' . $link . '</span> ';
402 }
403 }
404 $data['timestampLink'] = $link;
405
406 $currentAndLastLinks = '';
407 if ( $type == RC_EDIT || $type == RC_NEW ) {
408 $currentAndLastLinks .= ' ' . $this->msg( 'parentheses' )->rawParams(
409 $rcObj->curlink .
410 $this->message['pipe-separator'] .
411 $rcObj->lastlink
412 )->escaped();
413 }
414 $data['currentAndLastLinks'] = $currentAndLastLinks;
415 $data['separatorAfterCurrentAndLastLinks'] = $separator;
416
417 # Character diff
418 if ( $RCShowChangedSize ) {
419 $cd = $this->formatCharacterDifference( $rcObj );
420 if ( $cd !== '' ) {
421 $data['characterDiff'] = $cd;
422 $data['separatorAfterCharacterDiff'] = $separator;
423 }
424 }
425
426 if ( $type == RC_LOG ) {
427 $data['logEntry'] = $this->insertLogEntry( $rcObj );
428 } elseif ( $this->isCategorizationWithoutRevision( $rcObj ) ) {
429 $data['comment'] = $this->insertComment( $rcObj );
430 } else {
431 # User links
432 $data['userLink'] = $rcObj->userlink;
433 $data['userTalkLink'] = $rcObj->usertalklink;
434 $data['comment'] = $this->insertComment( $rcObj );
435 if ( $type == RC_CATEGORIZE ) {
436 $data['historyLink'] = $this->getDiffHistLinks( $rcObj, false );
437 }
438 # Rollback, thanks etc...
439 $data['rollback'] = $this->getRollback( $rcObj );
440 }
441
442 # Tags
443 $data['tags'] = $this->getTags( $rcObj, $classes );
444
445 $attribs = $this->getDataAttributes( $rcObj );
446
447 // give the hook a chance to modify the data
448 $success = $this->getHookRunner()->onEnhancedChangesListModifyLineData(
449 $this, $data, $block, $rcObj, $classes, $attribs );
450 if ( !$success ) {
451 // skip entry if hook aborted it
452 return [];
453 }
454 $attribs = array_filter( $attribs,
455 [ Sanitizer::class, 'isReservedDataAttribute' ],
456 ARRAY_FILTER_USE_KEY
457 );
458
459 $lineParams['recentChangesFlagsRaw'] = [];
460 if ( isset( $data['recentChangesFlags'] ) ) {
461 $lineParams['recentChangesFlags'] = $this->recentChangesFlags( $data['recentChangesFlags'] );
462 # FIXME: This is used by logic, don't return it in the template params.
463 $lineParams['recentChangesFlagsRaw'] = $data['recentChangesFlags'];
464 unset( $data['recentChangesFlags'] );
465 }
466
467 if ( isset( $data['timestampLink'] ) ) {
468 $lineParams['timestampLink'] = $data['timestampLink'];
469 unset( $data['timestampLink'] );
470 }
471
472 $lineParams['classes'] = array_values( $classes );
473 $lineParams['attribs'] = Html::expandAttributes( $attribs );
474
475 // everything else: makes it easier for extensions to add or remove data
476 $lineParams['data'] = array_values( $data );
477
478 return $lineParams;
479 }
480
491 protected function getLogText( $block, $queryParams, $allLogs, $isnew, $namehidden ) {
492 if ( !$block ) {
493 return '';
494 }
495
496 // Changes message
497 static $nchanges = [];
498 static $sinceLastVisitMsg = [];
499
500 $n = count( $block );
501 if ( !isset( $nchanges[$n] ) ) {
502 $nchanges[$n] = $this->msg( 'nchanges' )->numParams( $n )->escaped();
503 }
504
505 $sinceLast = 0;
506 $unvisitedOldid = null;
507 $currentRevision = 0;
508 $previousRevision = 0;
509 $curId = 0;
510 $allCategorization = true;
512 foreach ( $block as $rcObj ) {
513 // Fields of categorization entries refer to the changed page
514 // rather than the category for which we are building the log text.
515 if ( $rcObj->mAttribs['rc_type'] == RC_CATEGORIZE ) {
516 continue;
517 }
518
519 $allCategorization = false;
520 $previousRevision = $rcObj->mAttribs['rc_last_oldid'];
521 // Same logic as below inside main foreach
522 if ( $rcObj->watched ) {
523 $sinceLast++;
524 $unvisitedOldid = $previousRevision;
525 }
526 if ( !$currentRevision ) {
527 $currentRevision = $rcObj->mAttribs['rc_this_oldid'];
528 }
529 if ( !$curId ) {
530 $curId = $rcObj->mAttribs['rc_cur_id'];
531 }
532 }
533
534 // Total change link
535 $links = [];
536 $title = $block[0]->getTitle();
537 if ( !$allLogs ) {
538 // TODO: Disable the link if the user cannot see it (rc_deleted).
539 // Beware of possibly interspersed categorization entries.
540 if ( $isnew || $allCategorization ) {
541 $links['total-changes'] = Html::rawElement( 'span', [], $nchanges[$n] );
542 } else {
543 $links['total-changes'] = Html::rawElement( 'span', [],
544 $this->linkRenderer->makeKnownLink(
545 $title,
546 new HtmlArmor( $nchanges[$n] ),
547 [ 'class' => 'mw-changeslist-groupdiff' ],
548 $queryParams + [
549 'curid' => $curId,
550 'diff' => $currentRevision,
551 'oldid' => $previousRevision,
552 ]
553 )
554 );
555 }
556
557 if (
558 !$allCategorization &&
559 $sinceLast > 0 &&
560 $sinceLast < $n
561 ) {
562 if ( !isset( $sinceLastVisitMsg[$sinceLast] ) ) {
563 $sinceLastVisitMsg[$sinceLast] =
564 $this->msg( 'enhancedrc-since-last-visit' )->numParams( $sinceLast )->escaped();
565 }
566 $links['total-changes-since-last'] = Html::rawElement( 'span', [],
567 $this->linkRenderer->makeKnownLink(
568 $title,
569 new HtmlArmor( $sinceLastVisitMsg[$sinceLast] ),
570 [ 'class' => 'mw-changeslist-groupdiff' ],
571 $queryParams + [
572 'curid' => $curId,
573 'diff' => $currentRevision,
574 'oldid' => $unvisitedOldid,
575 ]
576 )
577 );
578 }
579 }
580
581 // History
582 if ( $allLogs || $allCategorization ) {
583 // don't show history link for logs
584 } elseif ( $namehidden || !$title->exists() ) {
585 $links['history'] = Html::rawElement( 'span', [], $this->message['enhancedrc-history'] );
586 } else {
587 $links['history'] = Html::rawElement( 'span', [],
588 $this->linkRenderer->makeKnownLink(
589 $title,
590 new HtmlArmor( $this->message['enhancedrc-history'] ),
591 [ 'class' => 'mw-changeslist-history' ],
592 [
593 'curid' => $curId,
594 'action' => 'history',
595 ] + $queryParams
596 )
597 );
598 }
599
600 // Allow others to alter, remove or add to these links
601 $this->getHookRunner()->onEnhancedChangesList__getLogText( $this, $links, $block );
602
603 if ( !$links ) {
604 return '';
605 }
606
607 $logtext = Html::rawElement( 'span', [ 'class' => 'mw-changeslist-links' ],
608 implode( ' ', $links ) );
609 return ' ' . $logtext;
610 }
611
618 protected function recentChangesBlockLine( $rcObj ) {
619 $data = [];
620
621 $type = $rcObj->mAttribs['rc_type'];
622 $logType = $rcObj->mAttribs['rc_log_type'];
623 $classes = $this->getHTMLClasses( $rcObj, $rcObj->watched );
624 $classes[] = 'mw-enhanced-rc';
625
626 if ( $logType ) {
627 # Log entry
628 $classes[] = 'mw-changeslist-log';
629 $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-' . $logType );
630 } else {
631 $classes[] = 'mw-changeslist-edit';
632 $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' .
633 $rcObj->mAttribs['rc_namespace'] . '-' . $rcObj->mAttribs['rc_title'] );
634 }
635
636 # Flag and Timestamp
637 $data['recentChangesFlags'] = [
638 'newpage' => $type == RC_NEW,
639 'minor' => $rcObj->mAttribs['rc_minor'],
640 'unpatrolled' => $rcObj->unpatrolled,
641 'bot' => $rcObj->mAttribs['rc_bot'],
642 ];
643 // timestamp is not really a link here, but is called timestampLink
644 // for consistency with EnhancedChangesListModifyLineData
645 $data['timestampLink'] = htmlspecialchars( $rcObj->timestamp );
646
647 # Article or log link
648 if ( $logType ) {
649 $logPage = new LogPage( $logType );
650 $logTitle = SpecialPage::getTitleFor( 'Log', $logType );
651 $logName = $logPage->getName()->text();
652 $data['logLink'] = Html::rawElement( 'span', [ 'class' => 'mw-changeslist-links' ],
653 $this->linkRenderer->makeKnownLink( $logTitle, $logName )
654 );
655 } else {
656 $data['articleLink'] = $this->getArticleLink( $rcObj, $rcObj->unpatrolled, $rcObj->watched );
657 }
658
659 # Diff and hist links
660 if ( $type != RC_LOG && $type != RC_CATEGORIZE ) {
661 $data['historyLink'] = $this->getDiffHistLinks( $rcObj, false );
662 }
663 $data['separatorAfterLinks'] = ' <span class="mw-changeslist-separator"></span> ';
664
665 # Character diff
666 if ( $this->getConfig()->get( MainConfigNames::RCShowChangedSize ) ) {
667 $cd = $this->formatCharacterDifference( $rcObj );
668 if ( $cd !== '' ) {
669 $data['characterDiff'] = $cd;
670 $data['separatorAftercharacterDiff'] = ' <span class="mw-changeslist-separator"></span> ';
671 }
672 }
673
674 if ( $type == RC_LOG ) {
675 $data['logEntry'] = $this->insertLogEntry( $rcObj );
676 } elseif ( $this->isCategorizationWithoutRevision( $rcObj ) ) {
677 $data['comment'] = $this->insertComment( $rcObj );
678 } else {
679 $data['userLink'] = $rcObj->userlink;
680 $data['userTalkLink'] = $rcObj->usertalklink;
681 $data['comment'] = $this->insertComment( $rcObj );
682 if ( $type == RC_CATEGORIZE ) {
683 $data['historyLink'] = $this->getDiffHistLinks( $rcObj, false );
684 }
685 $data['rollback'] = $this->getRollback( $rcObj );
686 }
687
688 # Tags
689 $data['tags'] = $this->getTags( $rcObj, $classes );
690
691 # Show how many people are watching this if enabled
692 $data['watchingUsers'] = $this->numberofWatchingusers( $rcObj->numberofWatchingusers );
693
694 $data['attribs'] = array_merge( $this->getDataAttributes( $rcObj ), [ 'class' => $classes ] );
695
696 // give the hook a chance to modify the data
697 $success = $this->getHookRunner()->onEnhancedChangesListModifyBlockLineData(
698 $this, $data, $rcObj );
699 if ( !$success ) {
700 // skip entry if hook aborted it
701 return '';
702 }
703 $attribs = $data['attribs'];
704 unset( $data['attribs'] );
705 $attribs = array_filter( $attribs, static function ( $key ) {
706 return $key === 'class' || Sanitizer::isReservedDataAttribute( $key );
707 }, ARRAY_FILTER_USE_KEY );
708
709 $prefix = '';
710 if ( is_callable( $this->changeLinePrefixer ) ) {
711 $prefix = ( $this->changeLinePrefixer )( $rcObj, $this, false );
712 }
713
714 $line = Html::openElement( 'table', $attribs ) . Html::openElement( 'tr' );
715 // Highlight block
716 $line .= Html::rawElement( 'td', [],
718 );
719
720 $line .= Html::rawElement( 'td', [], '<span class="mw-enhancedchanges-arrow-space"></span>' );
721 $line .= Html::rawElement( 'td', [ 'class' => 'mw-changeslist-line-prefix' ], $prefix );
722 $line .= '<td class="mw-enhanced-rc" colspan="2">';
723
724 if ( isset( $data['recentChangesFlags'] ) ) {
725 $line .= $this->recentChangesFlags( $data['recentChangesFlags'] );
726 unset( $data['recentChangesFlags'] );
727 }
728
729 if ( isset( $data['timestampLink'] ) ) {
730 $line .= "\u{00A0}" . $data['timestampLink'];
731 unset( $data['timestampLink'] );
732 }
733 $line .= "\u{00A0}</td>";
734 $line .= Html::openElement( 'td', [
735 'class' => 'mw-changeslist-line-inner',
736 // Used for reliable determination of the affiliated page
737 'data-target-page' => $rcObj->getTitle(),
738 ] );
739
740 // everything else: makes it easier for extensions to add or remove data
741 foreach ( $data as $key => $dataItem ) {
742 $line .= Html::rawElement( 'span', [
743 'class' => 'mw-changeslist-line-inner-' . $key,
744 ], $dataItem );
745 }
746
747 $line .= "</td></tr></table>\n";
748
749 return $line;
750 }
751
763 public function getDiffHistLinks( RCCacheEntry $rc, $query = null, $useParentheses = null ) {
764 if ( is_bool( $query ) ) {
765 $useParentheses = $query;
766 } elseif ( $query !== null ) {
767 wfDeprecated( __METHOD__ . ' with $query parameter', '1.36' );
768 }
769 $pageTitle = $rc->getTitle();
770 if ( $rc->getAttribute( 'rc_type' ) == RC_CATEGORIZE ) {
771 // For categorizations we must swap the category title with the page title!
772 $pageTitle = Title::newFromID( $rc->getAttribute( 'rc_cur_id' ) );
773 if ( !$pageTitle ) {
774 // The page has been deleted, but the RC entry
775 // deletion job has not run yet. Just skip.
776 return '';
777 }
778 }
779
780 $histLink = $this->linkRenderer->makeKnownLink(
781 $pageTitle,
782 new HtmlArmor( $this->message['hist'] ),
783 [ 'class' => 'mw-changeslist-history' ],
784 [
785 'curid' => $rc->getAttribute( 'rc_cur_id' ),
786 'action' => 'history'
787 ]
788 );
789 if ( $useParentheses !== false ) {
790 $retVal = $this->msg( 'parentheses' )
791 ->rawParams( $rc->difflink . $this->message['pipe-separator']
792 . $histLink )->escaped();
793 } else {
794 $retVal = Html::rawElement( 'span', [ 'class' => 'mw-changeslist-links' ],
795 Html::rawElement( 'span', [], $rc->difflink ) .
796 Html::rawElement( 'span', [], $histLink )
797 );
798 }
799 return ' ' . $retVal;
800 }
801
808 protected function recentChangesBlock() {
809 if ( count( $this->rc_cache ) == 0 ) {
810 return '';
811 }
812
813 $blockOut = '';
814 foreach ( $this->rc_cache as $block ) {
815 if ( count( $block ) < 2 ) {
816 $blockOut .= $this->recentChangesBlockLine( array_shift( $block ) );
817 } else {
818 $blockOut .= $this->recentChangesBlockGroup( $block );
819 }
820 }
821
822 if ( $blockOut === '' ) {
823 return '';
824 }
825 // $this->lastdate is kept up to date by recentChangesLine()
826 return Html::element( 'h4', [], $this->lastdate ) . "\n<div>" . $blockOut . '</div>';
827 }
828
834 public function endRecentChangesList() {
835 return $this->recentChangesBlock() . '</div>';
836 }
837}
838
840class_alias( EnhancedChangesList::class, 'EnhancedChangesList' );
const RC_NEW
Definition Defines.php:118
const RC_LOG
Definition Defines.php:119
const RC_EDIT
Definition Defines.php:117
const RC_CATEGORIZE
Definition Defines.php:121
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
This class is a collection of static functions that serve two purposes:
Definition Html.php:57
Class to simplify the use of log pages.
Definition LogPage.php:50
A class containing constants representing the names of configuration variables.
const RecentChangesFlags
Name constant for the RecentChangesFlags setting, for use with Config::get()
const RCShowChangedSize
Name constant for the RCShowChangedSize setting, for use with Config::get()
HTML sanitizer for MediaWiki.
Definition Sanitizer.php:46
Base class for lists of recent changes shown on special pages.
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.
getTags(RecentChange $rc, array &$classes)
getHighlightsContainerDiv()
Get the container for highlights that are used in the new StructuredFilters system.
insertLogEntry( $rc)
Insert a formatted action.
maybeWatchedLink( $link, $watched=false)
getArticleLink(&$rc, $unpatrolled, $watched)
Get the HTML link to the changed page, possibly with a prefix from hook handlers, and a suffix for te...
ChangesListFilterGroup[] $filterGroups
recentChangesFlags( $flags, $nothing="\u{00A0}")
Returns the appropriate flags for new page, minor change and patrolling.
static userCan( $rc, $field, ?Authority $performer=null)
Determine if the current user is allowed to view a particular field of this revision,...
insertComment( $rc)
Insert a formatted comment.
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.
formatCharacterDifference(RecentChange $old, ?RecentChange $new=null)
Format the character difference of one or several changes.
getDataAttributes(RecentChange $rc)
Get recommended data attributes for a change line.
Generate a list of changes using an Enhanced system (uses javascript).
getDiffHistLinks(RCCacheEntry $rc, $query=null, $useParentheses=null)
Returns value to be used in 'historyLink' element of $data param in EnhancedChangesListModifyBlockLin...
recentChangesBlock()
If enhanced RC is in use, this function takes the previously cached RC lines, arranges them,...
getLogText( $block, $queryParams, $allLogs, $isnew, $namehidden)
Generates amount of changes (linking to diff ) & link to history.
recentChangesBlockLine( $rcObj)
Enhanced RC ungrouped line.
recentChangesLine(&$rc, $watched=false, $linenumber=null)
Format a line for enhanced recentchange (aka with javascript and block of lines).
__construct( $context, array $filterGroups=[])
beginRecentChangesList()
Add the JavaScript file for enhanced changeslist.
endRecentChangesList()
Returns text for the end of RC If enhanced RC is in use, returns pretty much all the text.
getLineData(array $block, RCCacheEntry $rcObj, array $queryParams=[])
addCacheEntry(RCCacheEntry $cacheEntry)
Put accumulated information into the cache, for later display.
Create a RCCacheEntry from a RecentChange to use in EnhancedChangesList.
getAttribute( $name)
Get an attribute value.
Page revision base class.
Parent class for all special pages.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Represents a title within MediaWiki.
Definition Title.php:78
Marks HTML that shouldn't be escaped.
Definition HtmlArmor.php:32
Interface for objects which can provide a MediaWiki context on request.
element(SerializerNode $parent, SerializerNode $node, $contents)