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