MediaWiki  1.32.0
EnhancedChangesList.php
Go to the documentation of this file.
1 <?php
24 
28  protected $cacheEntryFactory;
29 
33  protected $rc_cache;
34 
38  protected $templateParser;
39 
45  public function __construct( $obj, array $filterGroups = [] ) {
46  if ( $obj instanceof Skin ) {
47  // @todo: deprecate constructing with Skin
48  $context = $obj->getContext();
49  } else {
50  if ( !$obj instanceof IContextSource ) {
51  throw new MWException( 'EnhancedChangesList must be constructed with a '
52  . 'context source or skin.' );
53  }
54 
55  $context = $obj;
56  }
57 
58  parent::__construct( $context, $filterGroups );
59 
60  // message is set by the parent ChangesList class
61  $this->cacheEntryFactory = new RCCacheEntryFactory(
62  $context,
63  $this->message,
64  $this->linkRenderer
65  );
66  $this->templateParser = new TemplateParser();
67  }
68 
73  public function beginRecentChangesList() {
74  $this->rc_cache = [];
75  $this->rcMoveIndex = 0;
76  $this->rcCacheIndex = 0;
77  $this->lastdate = '';
78  $this->rclistOpen = false;
79  $this->getOutput()->addModuleStyles( [
80  'mediawiki.special.changeslist',
81  'mediawiki.special.changeslist.enhanced',
82  ] );
83  $this->getOutput()->addModules( [
84  'jquery.makeCollapsible',
85  'mediawiki.icon',
86  ] );
87 
88  return '<div class="mw-changeslist">';
89  }
90 
100  public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) {
101  $date = $this->getLanguage()->userDate(
102  $rc->mAttribs['rc_timestamp'],
103  $this->getUser()
104  );
105  if ( $this->lastdate === '' ) {
106  $this->lastdate = $date;
107  }
108 
109  $ret = '';
110 
111  # If it's a new day, flush the cache and update $this->lastdate
112  if ( $date !== $this->lastdate ) {
113  # Process current cache (uses $this->lastdate to generate a heading)
114  $ret = $this->recentChangesBlock();
115  $this->rc_cache = [];
116  $this->lastdate = $date;
117  }
118 
119  $cacheEntry = $this->cacheEntryFactory->newFromRecentChange( $rc, $watched );
120  $this->addCacheEntry( $cacheEntry );
121 
122  return $ret;
123  }
124 
131  protected function addCacheEntry( RCCacheEntry $cacheEntry ) {
132  $cacheGroupingKey = $this->makeCacheGroupingKey( $cacheEntry );
133 
134  if ( !isset( $this->rc_cache[$cacheGroupingKey] ) ) {
135  $this->rc_cache[$cacheGroupingKey] = [];
136  }
137 
138  array_push( $this->rc_cache[$cacheGroupingKey], $cacheEntry );
139  }
140 
148  protected function makeCacheGroupingKey( RCCacheEntry $cacheEntry ) {
149  $title = $cacheEntry->getTitle();
150  $cacheGroupingKey = $title->getPrefixedDBkey();
151 
152  $type = $cacheEntry->mAttribs['rc_type'];
153 
154  if ( $type == RC_LOG ) {
155  // Group by log type
156  $cacheGroupingKey = SpecialPage::getTitleFor(
157  'Log',
158  $cacheEntry->mAttribs['rc_log_type']
159  )->getPrefixedDBkey();
160  }
161 
162  return $cacheGroupingKey;
163  }
164 
171  protected function recentChangesBlockGroup( $block ) {
172  $recentChangesFlags = $this->getConfig()->get( 'RecentChangesFlags' );
173 
174  # Add the namespace and title of the block as part of the class
175  $tableClasses = [ 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc', 'mw-changeslist-line' ];
176  if ( $block[0]->mAttribs['rc_log_type'] ) {
177  # Log entry
178  $tableClasses[] = 'mw-changeslist-log';
179  $tableClasses[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
180  . $block[0]->mAttribs['rc_log_type'] );
181  } else {
182  $tableClasses[] = 'mw-changeslist-edit';
183  $tableClasses[] = Sanitizer::escapeClass( 'mw-changeslist-ns'
184  . $block[0]->mAttribs['rc_namespace'] . '-' . $block[0]->mAttribs['rc_title'] );
185  }
186  if ( $block[0]->watched
187  && $block[0]->mAttribs['rc_timestamp'] >= $block[0]->watched
188  ) {
189  $tableClasses[] = 'mw-changeslist-line-watched';
190  } else {
191  $tableClasses[] = 'mw-changeslist-line-not-watched';
192  }
193 
194  # Collate list of users
195  $userlinks = [];
196  # Other properties
197  $curId = 0;
198  # Some catalyst variables...
199  $namehidden = true;
200  $allLogs = true;
201  $RCShowChangedSize = $this->getConfig()->get( 'RCShowChangedSize' );
202 
203  # Default values for RC flags
204  $collectedRcFlags = [];
205  foreach ( $recentChangesFlags as $key => $value ) {
206  $flagGrouping = ( $recentChangesFlags[$key]['grouping'] ?? 'any' );
207  switch ( $flagGrouping ) {
208  case 'all':
209  $collectedRcFlags[$key] = true;
210  break;
211  case 'any':
212  $collectedRcFlags[$key] = false;
213  break;
214  default:
215  throw new DomainException( "Unknown grouping type \"{$flagGrouping}\"" );
216  }
217  }
218  foreach ( $block as $rcObj ) {
219  // If all log actions to this page were hidden, then don't
220  // give the name of the affected page for this block!
221  if ( !$this->isDeleted( $rcObj, LogPage::DELETED_ACTION ) ) {
222  $namehidden = false;
223  }
224  $u = $rcObj->userlink;
225  if ( !isset( $userlinks[$u] ) ) {
226  $userlinks[$u] = 0;
227  }
228  if ( $rcObj->mAttribs['rc_type'] != RC_LOG ) {
229  $allLogs = false;
230  }
231  # Get the latest entry with a page_id and oldid
232  # since logs may not have these.
233  if ( !$curId && $rcObj->mAttribs['rc_cur_id'] ) {
234  $curId = $rcObj->mAttribs['rc_cur_id'];
235  }
236 
237  $userlinks[$u]++;
238  }
239 
240  # Sort the list and convert to text
241  krsort( $userlinks );
242  asort( $userlinks );
243  $users = [];
244  foreach ( $userlinks as $userlink => $count ) {
245  $text = $userlink;
246  $text .= $this->getLanguage()->getDirMark();
247  if ( $count > 1 ) {
248  $formattedCount = $this->msg( 'ntimes' )->numParams( $count )->escaped();
249  $text .= ' ' . $this->msg( 'parentheses' )->rawParams( $formattedCount )->escaped();
250  }
251  array_push( $users, $text );
252  }
253 
254  # Article link
255  $articleLink = '';
256  $revDeletedMsg = false;
257  if ( $namehidden ) {
258  $revDeletedMsg = $this->msg( 'rev-deleted-event' )->escaped();
259  } elseif ( $allLogs ) {
260  $articleLink = $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
261  } else {
262  $articleLink = $this->getArticleLink( $block[0], $block[0]->unpatrolled, $block[0]->watched );
263  }
264 
265  $queryParams['curid'] = $curId;
266 
267  # Sub-entries
268  $lines = [];
269  $filterClasses = [];
270  foreach ( $block as $i => $rcObj ) {
271  $line = $this->getLineData( $block, $rcObj, $queryParams );
272  if ( !$line ) {
273  // completely ignore this RC entry if we don't want to render it
274  unset( $block[$i] );
275  continue;
276  }
277 
278  // Roll up flags
279  foreach ( $line['recentChangesFlagsRaw'] as $key => $value ) {
280  $flagGrouping = ( $recentChangesFlags[$key]['grouping'] ?? 'any' );
281  switch ( $flagGrouping ) {
282  case 'all':
283  if ( !$value ) {
284  $collectedRcFlags[$key] = false;
285  }
286  break;
287  case 'any':
288  if ( $value ) {
289  $collectedRcFlags[$key] = true;
290  }
291  break;
292  default:
293  throw new DomainException( "Unknown grouping type \"{$flagGrouping}\"" );
294  }
295  }
296 
297  // Roll up filter-based CSS classes
298  $filterClasses = array_merge( $filterClasses, $this->getHTMLClassesForFilters( $rcObj ) );
299  // Add classes for change tags separately, getHTMLClassesForFilters() doesn't add them
300  $this->getTags( $rcObj, $filterClasses );
301  $filterClasses = array_unique( $filterClasses );
302 
303  $lines[] = $line;
304  }
305 
306  // Further down are some assumptions that $block is a 0-indexed array
307  // with (count-1) as last key. Let's make sure it is.
308  $block = array_values( $block );
309  $filterClasses = array_values( $filterClasses );
310 
311  if ( empty( $block ) || !$lines ) {
312  // if we can't show anything, don't display this block altogether
313  return '';
314  }
315 
316  $logText = $this->getLogText( $block, $queryParams, $allLogs,
317  $collectedRcFlags['newpage'], $namehidden
318  );
319 
320  # Character difference (does not apply if only log items)
321  $charDifference = false;
322  if ( $RCShowChangedSize && !$allLogs ) {
323  $last = 0;
324  $first = count( $block ) - 1;
325  # Some events (like logs and category changes) have an "empty" size, so we need to skip those...
326  while ( $last < $first && $block[$last]->mAttribs['rc_new_len'] === null ) {
327  $last++;
328  }
329  while ( $last < $first && $block[$first]->mAttribs['rc_old_len'] === null ) {
330  $first--;
331  }
332  # Get net change
333  $charDifference = $this->formatCharacterDifference( $block[$first], $block[$last] ) ?: false;
334  }
335 
336  $numberofWatchingusers = $this->numberofWatchingusers( $block[0]->numberofWatchingusers );
337  $usersList = $this->msg( 'brackets' )->rawParams(
338  implode( $this->message['semicolon-separator'], $users )
339  )->escaped();
340 
341  $prefix = '';
342  if ( is_callable( $this->changeLinePrefixer ) ) {
343  $prefix = call_user_func( $this->changeLinePrefixer, $block[0], $this, true );
344  }
345 
346  $templateParams = [
347  'articleLink' => $articleLink,
348  'charDifference' => $charDifference,
349  'collectedRcFlags' => $this->recentChangesFlags( $collectedRcFlags ),
350  'filterClasses' => $filterClasses,
351  'languageDirMark' => $this->getLanguage()->getDirMark(),
352  'lines' => $lines,
353  'logText' => $logText,
354  'numberofWatchingusers' => $numberofWatchingusers,
355  'prefix' => $prefix,
356  'rev-deleted-event' => $revDeletedMsg,
357  'tableClasses' => $tableClasses,
358  'timestamp' => $block[0]->timestamp,
359  'fullTimestamp' => $block[0]->getAttribute( 'rc_timestamp' ),
360  'users' => $usersList,
361  ];
362 
363  $this->rcCacheIndex++;
364 
365  return $this->templateParser->processTemplate(
366  'EnhancedChangesListGroup',
367  $templateParams
368  );
369  }
370 
380  protected function getLineData( array $block, RCCacheEntry $rcObj, array $queryParams = [] ) {
381  $RCShowChangedSize = $this->getConfig()->get( 'RCShowChangedSize' );
382 
383  $type = $rcObj->mAttribs['rc_type'];
384  $data = [];
385  $lineParams = [ 'targetTitle' => $rcObj->getTitle() ];
386 
387  $classes = [ 'mw-enhanced-rc' ];
388  if ( $rcObj->watched
389  && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
390  ) {
391  $classes[] = 'mw-enhanced-watched';
392  }
393  $classes = array_merge( $classes, $this->getHTMLClasses( $rcObj, $rcObj->watched ) );
394 
395  $separator = ' <span class="mw-changeslist-separator">. .</span> ';
396 
397  $data['recentChangesFlags'] = [
398  'newpage' => $type == RC_NEW,
399  'minor' => $rcObj->mAttribs['rc_minor'],
400  'unpatrolled' => $rcObj->unpatrolled,
401  'bot' => $rcObj->mAttribs['rc_bot'],
402  ];
403 
404  $params = $queryParams;
405 
406  if ( $rcObj->mAttribs['rc_this_oldid'] != 0 ) {
407  $params['oldid'] = $rcObj->mAttribs['rc_this_oldid'];
408  }
409 
410  # Log timestamp
411  if ( $type == RC_LOG ) {
412  $link = htmlspecialchars( $rcObj->timestamp );
413  # Revision link
414  } elseif ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
415  $link = Html::element( 'span', [ 'class' => 'history-deleted' ], $rcObj->timestamp );
416  } else {
417  $link = $this->linkRenderer->makeKnownLink(
418  $rcObj->getTitle(),
419  $rcObj->timestamp,
420  [],
421  $params
422  );
423  if ( $this->isDeleted( $rcObj, Revision::DELETED_TEXT ) ) {
424  $link = '<span class="history-deleted">' . $link . '</span> ';
425  }
426  }
427  $data['timestampLink'] = $link;
428 
429  $currentAndLastLinks = '';
430  if ( !$type == RC_LOG || $type == RC_NEW ) {
431  $currentAndLastLinks .= ' ' . $this->msg( 'parentheses' )->rawParams(
432  $rcObj->curlink .
433  $this->message['pipe-separator'] .
434  $rcObj->lastlink
435  )->escaped();
436  }
437  $data['currentAndLastLinks'] = $currentAndLastLinks;
438  $data['separatorAfterCurrentAndLastLinks'] = $separator;
439 
440  # Character diff
441  if ( $RCShowChangedSize ) {
442  $cd = $this->formatCharacterDifference( $rcObj );
443  if ( $cd !== '' ) {
444  $data['characterDiff'] = $cd;
445  $data['separatorAfterCharacterDiff'] = $separator;
446  }
447  }
448 
449  if ( $rcObj->mAttribs['rc_type'] == RC_LOG ) {
450  $data['logEntry'] = $this->insertLogEntry( $rcObj );
451  } elseif ( $this->isCategorizationWithoutRevision( $rcObj ) ) {
452  $data['comment'] = $this->insertComment( $rcObj );
453  } else {
454  # User links
455  $data['userLink'] = $rcObj->userlink;
456  $data['userTalkLink'] = $rcObj->usertalklink;
457  $data['comment'] = $this->insertComment( $rcObj );
458  }
459 
460  # Rollback
461  $data['rollback'] = $this->getRollback( $rcObj );
462 
463  # Tags
464  $data['tags'] = $this->getTags( $rcObj, $classes );
465 
466  $attribs = $this->getDataAttributes( $rcObj );
467 
468  // give the hook a chance to modify the data
469  $success = Hooks::run( 'EnhancedChangesListModifyLineData',
470  [ $this, &$data, $block, $rcObj, &$classes, &$attribs ] );
471  if ( !$success ) {
472  // skip entry if hook aborted it
473  return [];
474  }
475  $attribs = array_filter( $attribs,
476  [ Sanitizer::class, 'isReservedDataAttribute' ],
477  ARRAY_FILTER_USE_KEY
478  );
479 
480  $lineParams['recentChangesFlagsRaw'] = [];
481  if ( isset( $data['recentChangesFlags'] ) ) {
482  $lineParams['recentChangesFlags'] = $this->recentChangesFlags( $data['recentChangesFlags'] );
483  # FIXME: This is used by logic, don't return it in the template params.
484  $lineParams['recentChangesFlagsRaw'] = $data['recentChangesFlags'];
485  unset( $data['recentChangesFlags'] );
486  }
487 
488  if ( isset( $data['timestampLink'] ) ) {
489  $lineParams['timestampLink'] = $data['timestampLink'];
490  unset( $data['timestampLink'] );
491  }
492 
493  $lineParams['classes'] = array_values( $classes );
494  $lineParams['attribs'] = Html::expandAttributes( $attribs );
495 
496  // everything else: makes it easier for extensions to add or remove data
497  $lineParams['data'] = array_values( $data );
498 
499  return $lineParams;
500  }
501 
512  protected function getLogText( $block, $queryParams, $allLogs, $isnew, $namehidden ) {
513  if ( empty( $block ) ) {
514  return '';
515  }
516 
517  # Changes message
518  static $nchanges = [];
519  static $sinceLastVisitMsg = [];
520 
521  $n = count( $block );
522  if ( !isset( $nchanges[$n] ) ) {
523  $nchanges[$n] = $this->msg( 'nchanges' )->numParams( $n )->escaped();
524  }
525 
526  $sinceLast = 0;
527  $unvisitedOldid = null;
529  foreach ( $block as $rcObj ) {
530  // Same logic as below inside main foreach
531  if ( $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched ) {
532  $sinceLast++;
533  $unvisitedOldid = $rcObj->mAttribs['rc_last_oldid'];
534  }
535  }
536  if ( !isset( $sinceLastVisitMsg[$sinceLast] ) ) {
537  $sinceLastVisitMsg[$sinceLast] =
538  $this->msg( 'enhancedrc-since-last-visit' )->numParams( $sinceLast )->escaped();
539  }
540 
541  $currentRevision = 0;
542  foreach ( $block as $rcObj ) {
543  if ( !$currentRevision ) {
544  $currentRevision = $rcObj->mAttribs['rc_this_oldid'];
545  }
546  }
547 
548  # Total change link
549  $links = [];
551  $block0 = $block[0];
552  $last = $block[count( $block ) - 1];
553  if ( !$allLogs ) {
554  if ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ||
555  $isnew ||
556  $rcObj->mAttribs['rc_type'] == RC_CATEGORIZE
557  ) {
558  $links['total-changes'] = $nchanges[$n];
559  } else {
560  $links['total-changes'] = $this->linkRenderer->makeKnownLink(
561  $block0->getTitle(),
562  new HtmlArmor( $nchanges[$n] ),
563  [ 'class' => 'mw-changeslist-groupdiff' ],
564  $queryParams + [
565  'diff' => $currentRevision,
566  'oldid' => $last->mAttribs['rc_last_oldid'],
567  ]
568  );
569  if ( $sinceLast > 0 && $sinceLast < $n ) {
570  $links['total-changes-since-last'] = $this->linkRenderer->makeKnownLink(
571  $block0->getTitle(),
572  new HtmlArmor( $sinceLastVisitMsg[$sinceLast] ),
573  [ 'class' => 'mw-changeslist-groupdiff' ],
574  $queryParams + [
575  'diff' => $currentRevision,
576  'oldid' => $unvisitedOldid,
577  ]
578  );
579  }
580  }
581  }
582 
583  # History
584  if ( $allLogs || $rcObj->mAttribs['rc_type'] == RC_CATEGORIZE ) {
585  // don't show history link for logs
586  } elseif ( $namehidden || !$block0->getTitle()->exists() ) {
587  $links['history'] = $this->message['enhancedrc-history'];
588  } else {
589  $params = $queryParams;
590  $params['action'] = 'history';
591 
592  $links['history'] = $this->linkRenderer->makeKnownLink(
593  $block0->getTitle(),
594  new HtmlArmor( $this->message['enhancedrc-history'] ),
595  [ 'class' => 'mw-changeslist-history' ],
596  $params
597  );
598  }
599 
600  # Allow others to alter, remove or add to these links
601  Hooks::run( 'EnhancedChangesList::getLogText',
602  [ $this, &$links, $block ] );
603 
604  if ( !$links ) {
605  return '';
606  }
607 
608  $logtext = implode( $this->message['pipe-separator'], $links );
609  $logtext = $this->msg( 'parentheses' )->rawParams( $logtext )->escaped();
610  return ' ' . $logtext;
611  }
612 
619  protected function recentChangesBlockLine( $rcObj ) {
620  $data = [];
621 
622  $query['curid'] = $rcObj->mAttribs['rc_cur_id'];
623 
624  $type = $rcObj->mAttribs['rc_type'];
625  $logType = $rcObj->mAttribs['rc_log_type'];
626  $classes = $this->getHTMLClasses( $rcObj, $rcObj->watched );
627  $classes[] = 'mw-enhanced-rc';
628 
629  if ( $logType ) {
630  # Log entry
631  $classes[] = 'mw-changeslist-log';
632  $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-' . $logType );
633  } else {
634  $classes[] = 'mw-changeslist-edit';
635  $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' .
636  $rcObj->mAttribs['rc_namespace'] . '-' . $rcObj->mAttribs['rc_title'] );
637  }
638 
639  # Flag and Timestamp
640  $data['recentChangesFlags'] = [
641  'newpage' => $type == RC_NEW,
642  'minor' => $rcObj->mAttribs['rc_minor'],
643  'unpatrolled' => $rcObj->unpatrolled,
644  'bot' => $rcObj->mAttribs['rc_bot'],
645  ];
646  // timestamp is not really a link here, but is called timestampLink
647  // for consistency with EnhancedChangesListModifyLineData
648  $data['timestampLink'] = htmlspecialchars( $rcObj->timestamp );
649 
650  # Article or log link
651  if ( $logType ) {
652  $logPage = new LogPage( $logType );
653  $logTitle = SpecialPage::getTitleFor( 'Log', $logType );
654  $logName = $logPage->getName()->text();
655  $data['logLink'] = $this->msg( 'parentheses' )
656  ->rawParams(
657  $this->linkRenderer->makeKnownLink( $logTitle, $logName )
658  )->escaped();
659  } else {
660  $data['articleLink'] = $this->getArticleLink( $rcObj, $rcObj->unpatrolled, $rcObj->watched );
661  }
662 
663  # Diff and hist links
664  if ( $type != RC_LOG && $type != RC_CATEGORIZE ) {
665  $query['action'] = 'history';
666  $data['historyLink'] = $this->getDiffHistLinks( $rcObj, $query );
667  }
668  $data['separatorAfterLinks'] = ' <span class="mw-changeslist-separator">. .</span> ';
669 
670  # Character diff
671  if ( $this->getConfig()->get( 'RCShowChangedSize' ) ) {
672  $cd = $this->formatCharacterDifference( $rcObj );
673  if ( $cd !== '' ) {
674  $data['characterDiff'] = $cd;
675  $data['separatorAftercharacterDiff'] = ' <span class="mw-changeslist-separator">. .</span> ';
676  }
677  }
678 
679  if ( $type == RC_LOG ) {
680  $data['logEntry'] = $this->insertLogEntry( $rcObj );
681  } elseif ( $this->isCategorizationWithoutRevision( $rcObj ) ) {
682  $data['comment'] = $this->insertComment( $rcObj );
683  } else {
684  $data['userLink'] = $rcObj->userlink;
685  $data['userTalkLink'] = $rcObj->usertalklink;
686  $data['comment'] = $this->insertComment( $rcObj );
687  if ( $type == RC_CATEGORIZE ) {
688  $data['historyLink'] = $this->getDiffHistLinks( $rcObj, $query );
689  }
690  $data['rollback'] = $this->getRollback( $rcObj );
691  }
692 
693  # Tags
694  $data['tags'] = $this->getTags( $rcObj, $classes );
695 
696  # Show how many people are watching this if enabled
697  $data['watchingUsers'] = $this->numberofWatchingusers( $rcObj->numberofWatchingusers );
698 
699  $data['attribs'] = array_merge( $this->getDataAttributes( $rcObj ), [ 'class' => $classes ] );
700 
701  // give the hook a chance to modify the data
702  $success = Hooks::run( 'EnhancedChangesListModifyBlockLineData',
703  [ $this, &$data, $rcObj ] );
704  if ( !$success ) {
705  // skip entry if hook aborted it
706  return '';
707  }
708  $attribs = $data['attribs'];
709  unset( $data['attribs'] );
710  $attribs = array_filter( $attribs, function ( $key ) {
711  return $key === 'class' || Sanitizer::isReservedDataAttribute( $key );
712  }, ARRAY_FILTER_USE_KEY );
713 
714  $prefix = '';
715  if ( is_callable( $this->changeLinePrefixer ) ) {
716  $prefix = call_user_func( $this->changeLinePrefixer, $rcObj, $this, false );
717  }
718 
719  $line = Html::openElement( 'table', $attribs ) . Html::openElement( 'tr' );
720  // Highlight block
721  $line .= Html::rawElement( 'td', [],
723  );
724 
725  $line .= Html::rawElement( 'td', [], '<span class="mw-enhancedchanges-arrow-space"></span>' );
726  $line .= Html::rawElement( 'td', [ 'class' => 'mw-changeslist-line-prefix' ], $prefix );
727  $line .= '<td class="mw-enhanced-rc" colspan="2">';
728 
729  if ( isset( $data['recentChangesFlags'] ) ) {
730  $line .= $this->recentChangesFlags( $data['recentChangesFlags'] );
731  unset( $data['recentChangesFlags'] );
732  }
733 
734  if ( isset( $data['timestampLink'] ) ) {
735  $line .= "\u{00A0}" . $data['timestampLink'];
736  unset( $data['timestampLink'] );
737  }
738  $line .= "\u{00A0}</td>";
739  $line .= Html::openElement( 'td', [
740  'class' => 'mw-changeslist-line-inner',
741  // Used for reliable determination of the affiliated page
742  'data-target-page' => $rcObj->getTitle(),
743  ] );
744 
745  // everything else: makes it easier for extensions to add or remove data
746  $line .= implode( '', $data );
747 
748  $line .= "</td></tr></table>\n";
749 
750  return $line;
751  }
752 
763  public function getDiffHistLinks( RCCacheEntry $rc, array $query ) {
764  $pageTitle = $rc->getTitle();
765  if ( $rc->getAttribute( 'rc_type' ) == RC_CATEGORIZE ) {
766  // For categorizations we must swap the category title with the page title!
767  $pageTitle = Title::newFromID( $rc->getAttribute( 'rc_cur_id' ) );
768  if ( !$pageTitle ) {
769  // The page has been deleted, but the RC entry
770  // deletion job has not run yet. Just skip.
771  return '';
772  }
773  }
774 
775  $retVal = ' ' . $this->msg( 'parentheses' )
776  ->rawParams( $rc->difflink . $this->message['pipe-separator']
777  . $this->linkRenderer->makeKnownLink(
778  $pageTitle,
779  new HtmlArmor( $this->message['hist'] ),
780  [ 'class' => 'mw-changeslist-history' ],
781  $query
782  ) )->escaped();
783  return $retVal;
784  }
785 
792  protected function recentChangesBlock() {
793  if ( count( $this->rc_cache ) == 0 ) {
794  return '';
795  }
796 
797  $blockOut = '';
798  foreach ( $this->rc_cache as $block ) {
799  if ( count( $block ) < 2 ) {
800  $blockOut .= $this->recentChangesBlockLine( array_shift( $block ) );
801  } else {
802  $blockOut .= $this->recentChangesBlockGroup( $block );
803  }
804  }
805 
806  if ( $blockOut === '' ) {
807  return '';
808  }
809  // $this->lastdate is kept up to date by recentChangesLine()
810  return Xml::element( 'h4', null, $this->lastdate ) . "\n<div>" . $blockOut . '</div>';
811  }
812 
818  public function endRecentChangesList() {
819  return $this->recentChangesBlock() . '</div>';
820  }
821 }
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:33
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:63
HtmlArmor
Marks HTML that shouldn't be escaped.
Definition: HtmlArmor.php:28
EnhancedChangesList\recentChangesLine
recentChangesLine(&$rc, $watched=false, $linenumber=null)
Format a line for enhanced recentchange (aka with javascript and block of lines).
Definition: EnhancedChangesList.php:100
ChangesList\insertComment
insertComment( $rc)
Insert a formatted comment.
Definition: ChangesList.php:587
captcha-old.count
count
Definition: captcha-old.py:249
$last
$last
Definition: profileinfo.php:419
ContextSource\msg
msg( $key)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:168
ChangesList\maybeWatchedLink
maybeWatchedLink( $link, $watched=false)
Definition: ChangesList.php:646
RC_LOG
const RC_LOG
Definition: Defines.php:144
EnhancedChangesList\getDiffHistLinks
getDiffHistLinks(RCCacheEntry $rc, array $query)
Returns value to be used in 'historyLink' element of $data param in EnhancedChangesListModifyBlockLin...
Definition: EnhancedChangesList.php:763
ChangesList\getTags
getTags(RecentChange $rc, array &$classes)
Definition: ChangesList.php:719
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:475
$params
$params
Definition: styleTest.css.php:44
EnhancedChangesList\makeCacheGroupingKey
makeCacheGroupingKey(RCCacheEntry $cacheEntry)
Definition: EnhancedChangesList.php:148
ChangesList\getHighlightsContainerDiv
getHighlightsContainerDiv()
Get the container for highlights that are used in the new StructuredFilters system.
Definition: ChangesList.php:124
link
usually copyright or history_copyright This message must be in HTML not wikitext if the section is included from a template to be included in the link
Definition: hooks.txt:3090
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:82
RCCacheEntryFactory
Definition: RCCacheEntryFactory.php:24
$success
$success
Definition: NoLocalSettings.php:42
ChangesList\formatCharacterDifference
formatCharacterDifference(RecentChange $old, RecentChange $new=null)
Format the character difference of one or several changes.
Definition: ChangesList.php:369
ContextSource\getUser
getUser()
Definition: ContextSource.php:120
ChangesList\isDeleted
static isDeleted( $rc, $field)
Determine if said field of a revision is hidden.
Definition: ChangesList.php:621
EnhancedChangesList\$cacheEntryFactory
RCCacheEntryFactory $cacheEntryFactory
Definition: EnhancedChangesList.php:28
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
ContextSource\getLanguage
getLanguage()
Definition: ContextSource.php:128
EnhancedChangesList\getLineData
getLineData(array $block, RCCacheEntry $rcObj, array $queryParams=[])
Definition: EnhancedChangesList.php:380
ChangesList\$filterGroups
array $filterGroups
Definition: ChangesList.php:58
$query
null for the wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition: hooks.txt:1627
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:818
MWException
MediaWiki exception.
Definition: MWException.php:26
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:964
ChangesList\insertLogEntry
insertLogEntry( $rc)
Insert a formatted action.
Definition: ChangesList.php:573
ContextSource\getOutput
getOutput()
Definition: ContextSource.php:112
$attribs
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing & $attribs
Definition: hooks.txt:2036
LogPage
Class to simplify the use of log pages.
Definition: LogPage.php:33
EnhancedChangesList\beginRecentChangesList
beginRecentChangesList()
Add the JavaScript file for enhanced changeslist.
Definition: EnhancedChangesList.php:73
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
$lines
$lines
Definition: router.php:61
EnhancedChangesList\recentChangesBlockGroup
recentChangesBlockGroup( $block)
Enhanced RC group.
Definition: EnhancedChangesList.php:171
array
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
EnhancedChangesList\__construct
__construct( $obj, array $filterGroups=[])
Definition: EnhancedChangesList.php:45
ChangesList\getRollback
getRollback(RecentChange $rc)
Definition: ChangesList.php:688
LogPage\DELETED_ACTION
const DELETED_ACTION
Definition: LogPage.php:34
ChangesList\getArticleLink
getArticleLink(&$rc, $unpatrolled, $watched)
Definition: ChangesList.php:494
ChangesList\getDataAttributes
getDataAttributes(RecentChange $rc)
Get recommended data attributes for a change line.
Definition: ChangesList.php:783
$line
$line
Definition: cdb.php:59
EnhancedChangesList
Definition: EnhancedChangesList.php:23
EnhancedChangesList\getLogText
getLogText( $block, $queryParams, $allLogs, $isnew, $namehidden)
Generates amount of changes (linking to diff ) & link to history.
Definition: EnhancedChangesList.php:512
$value
$value
Definition: styleTest.css.php:49
EnhancedChangesList\$rc_cache
array $rc_cache
Array of array of RCCacheEntry.
Definition: EnhancedChangesList.php:33
EnhancedChangesList\$templateParser
TemplateParser $templateParser
Definition: EnhancedChangesList.php:38
ChangesList\numberofWatchingusers
numberofWatchingusers( $count)
Returns the string which indicates the number of watching users.
Definition: ChangesList.php:601
EnhancedChangesList\recentChangesBlock
recentChangesBlock()
If enhanced RC is in use, this function takes the previously cached RC lines, arranges them,...
Definition: EnhancedChangesList.php:792
$ret
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:2036
RC_NEW
const RC_NEW
Definition: Defines.php:143
message
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation use $formDescriptor instead default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a message
Definition: hooks.txt:2205
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:53
ChangesList\getHTMLClasses
getHTMLClasses( $rc, $watched)
Get an array of default HTML class attributes for the change.
Definition: ChangesList.php:199
RecentChange\getAttribute
getAttribute( $name)
Get an attribute value.
Definition: RecentChange.php:1070
ChangesList\recentChangesFlags
recentChangesFlags( $flags, $nothing="\u{00A0}")
Returns the appropriate flags for new page, minor change and patrolling.
Definition: ChangesList.php:180
RCCacheEntry
Definition: RCCacheEntry.php:21
EnhancedChangesList\recentChangesBlockLine
recentChangesBlockLine( $rcObj)
Enhanced RC ungrouped line.
Definition: EnhancedChangesList.php:619
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:773
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
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
$link
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition: hooks.txt:3090
ChangesList
Definition: ChangesList.php:28
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
RC_CATEGORIZE
const RC_CATEGORIZE
Definition: Defines.php:146
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:232
Skin
The main skin class which provides methods and properties for all other skins.
Definition: Skin.php:38
RecentChange\getTitle
& getTitle()
Definition: RecentChange.php:330
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:427
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
Revision\DELETED_TEXT
const DELETED_TEXT
Definition: Revision.php:47
ChangesList\getHTMLClassesForFilters
getHTMLClassesForFilters( $rc)
Get an array of CSS classes attributed to filters for this row.
Definition: ChangesList.php:230
ChangesList\userCan
static userCan( $rc, $field, User $user=null)
Determine if the current user is allowed to view a particular field of this revision,...
Definition: ChangesList.php:633
EnhancedChangesList\addCacheEntry
addCacheEntry(RCCacheEntry $cacheEntry)
Put accumulated information into the cache, for later display.
Definition: EnhancedChangesList.php:131
$type
$type
Definition: testCompression.php:48