MediaWiki  master
ContribsPager.php
Go to the documentation of this file.
1 <?php
26 use Wikimedia\IPUtils;
30 
36 
40  private $messages;
41 
45  private $target;
46 
50  private $namespace = '';
51 
55  private $tagFilter;
56 
60  private $nsInvert;
61 
66  private $associated;
67 
71  private $deletedOnly;
72 
76  private $topOnly;
77 
81  private $newOnly;
82 
86  private $hideMinor;
87 
92  private $revisionsOnly;
93 
94  private $preventClickjacking = false;
95 
99  private $mParentLens;
100 
105 
108 
109  public function __construct(
111  array $options,
114  ) {
115  // Set ->target before calling parent::__construct() so
116  // parent can call $this->getIndexField() and get the right result. Set
117  // the rest too just to keep things simple.
118  $this->target = $options['target'] ?? '';
119  $this->namespace = $options['namespace'] ?? '';
120  $this->tagFilter = $options['tagfilter'] ?? false;
121  $this->nsInvert = $options['nsInvert'] ?? false;
122  $this->associated = $options['associated'] ?? false;
123 
124  $this->deletedOnly = !empty( $options['deletedOnly'] );
125  $this->topOnly = !empty( $options['topOnly'] );
126  $this->newOnly = !empty( $options['newOnly'] );
127  $this->hideMinor = !empty( $options['hideMinor'] );
128  $this->revisionsOnly = !empty( $options['revisionsOnly'] );
129 
130  parent::__construct( $context, $linkRenderer );
131 
132  $msgs = [
133  'diff',
134  'hist',
135  'pipe-separator',
136  'uctop'
137  ];
138 
139  foreach ( $msgs as $msg ) {
140  $this->messages[$msg] = $this->msg( $msg )->escaped();
141  }
142 
143  // Date filtering: use timestamp if available
144  $startTimestamp = '';
145  $endTimestamp = '';
146  if ( isset( $options['start'] ) && $options['start'] ) {
147  $startTimestamp = $options['start'] . ' 00:00:00';
148  }
149  if ( isset( $options['end'] ) && $options['end'] ) {
150  $endTimestamp = $options['end'] . ' 23:59:59';
151  }
152  $this->getDateRangeCond( $startTimestamp, $endTimestamp );
153 
154  // Most of this code will use the 'contributions' group DB, which can map to replica DBs
155  // with extra user based indexes or partioning by user.
156  $this->mDb = wfGetDB( DB_REPLICA, 'contributions' );
157  $this->templateParser = new TemplateParser();
158  $this->linkBatchFactory = $linkBatchFactory ?? MediaWikiServices::getInstance()->getLinkBatchFactory();
159  }
160 
161  public function getDefaultQuery() {
162  $query = parent::getDefaultQuery();
163  $query['target'] = $this->target;
164 
165  return $query;
166  }
167 
175  public function getNavigationBar() {
176  return Html::rawElement( 'p', [ 'class' => 'mw-pager-navigation-bar' ],
177  parent::getNavigationBar()
178  );
179  }
180 
190  public function reallyDoQuery( $offset, $limit, $order ) {
191  list( $tables, $fields, $conds, $fname, $options, $join_conds ) = $this->buildQueryInfo(
192  $offset,
193  $limit,
194  $order
195  );
196 
197  /*
198  * This hook will allow extensions to add in additional queries, so they can get their data
199  * in My Contributions as well. Extensions should append their results to the $data array.
200  *
201  * Extension queries have to implement the navbar requirement as well. They should
202  * - have a column aliased as $pager->getIndexField()
203  * - have LIMIT set
204  * - have a WHERE-clause that compares the $pager->getIndexField()-equivalent column to the offset
205  * - have the ORDER BY specified based upon the details provided by the navbar
206  *
207  * See includes/Pager.php buildQueryInfo() method on how to build LIMIT, WHERE & ORDER BY
208  *
209  * &$data: an array of results of all contribs queries
210  * $pager: the ContribsPager object hooked into
211  * $offset: see phpdoc above
212  * $limit: see phpdoc above
213  * $descending: see phpdoc above
214  */
215  $data = [ $this->mDb->select(
216  $tables, $fields, $conds, $fname, $options, $join_conds
217  ) ];
218  if ( !$this->revisionsOnly ) {
219  $this->getHookRunner()->onContribsPager__reallyDoQuery(
220  $data, $this, $offset, $limit, $order );
221  }
222 
223  $result = [];
224 
225  // loop all results and collect them in an array
226  foreach ( $data as $query ) {
227  foreach ( $query as $i => $row ) {
228  // If the query results are in descending order, the indexes must also be in descending order
229  $index = $order === self::QUERY_ASCENDING ? $i : $limit - 1 - $i;
230  // Left-pad with zeroes, because these values will be sorted as strings
231  $index = str_pad( $index, strlen( $limit ), '0', STR_PAD_LEFT );
232  // use index column as key, allowing us to easily sort in PHP
233  $result[$row->{$this->getIndexField()} . "-$index"] = $row;
234  }
235  }
236 
237  // sort results
238  if ( $order === self::QUERY_ASCENDING ) {
239  ksort( $result );
240  } else {
241  krsort( $result );
242  }
243 
244  // enforce limit
245  $result = array_slice( $result, 0, $limit );
246 
247  // get rid of array keys
248  $result = array_values( $result );
249 
250  return new FakeResultWrapper( $result );
251  }
252 
262  private function getTargetTable() {
263  $user = User::newFromName( $this->target, false );
264  $ipRangeConds = $user->isAnon() ? $this->getIpRangeConds( $this->mDb, $this->target ) : null;
265  if ( $ipRangeConds ) {
266  return 'ip_changes';
267  } else {
268  $conds = ActorMigration::newMigration()->getWhere( $this->mDb, 'rev_user', $user );
269  if ( isset( $conds['orconds']['actor'] ) ) {
270  // @todo: This will need changing when revision_actor_temp goes away
271  return 'revision_actor_temp';
272  }
273  }
274 
275  return 'revision';
276  }
277 
278  public function getQueryInfo() {
279  $revQuery = MediaWikiServices::getInstance()
280  ->getRevisionStore()
281  ->getQueryInfo( [ 'page', 'user' ] );
282  $queryInfo = [
283  'tables' => $revQuery['tables'],
284  'fields' => array_merge( $revQuery['fields'], [ 'page_is_new' ] ),
285  'conds' => [],
286  'options' => [],
287  'join_conds' => $revQuery['joins'],
288  ];
289  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
290 
291  // WARNING: Keep this in sync with getTargetTable()!
292  $user = User::newFromName( $this->target, false );
293  $ipRangeConds = $user->isAnon() ? $this->getIpRangeConds( $this->mDb, $this->target ) : null;
294  if ( $ipRangeConds ) {
295  $queryInfo['tables'][] = 'ip_changes';
296  $queryInfo['join_conds']['ip_changes'] = [
297  'LEFT JOIN', [ 'ipc_rev_id = rev_id' ]
298  ];
299  $queryInfo['conds'][] = $ipRangeConds;
300  } else {
301  // tables and joins are already handled by Revision::getQueryInfo()
302  $conds = ActorMigration::newMigration()->getWhere( $this->mDb, 'rev_user', $user );
303  $queryInfo['conds'][] = $conds['conds'];
304  // Force the appropriate index to avoid bad query plans (T189026)
305  if ( isset( $conds['orconds']['actor'] ) ) {
306  // @todo: This will need changing when revision_actor_temp goes away
307  $queryInfo['options']['USE INDEX']['temp_rev_user'] = 'actor_timestamp';
308  }
309  }
310 
311  if ( $this->deletedOnly ) {
312  $queryInfo['conds'][] = 'rev_deleted != 0';
313  }
314 
315  if ( $this->topOnly ) {
316  $queryInfo['conds'][] = 'rev_id = page_latest';
317  }
318 
319  if ( $this->newOnly ) {
320  $queryInfo['conds'][] = 'rev_parent_id = 0';
321  }
322 
323  if ( $this->hideMinor ) {
324  $queryInfo['conds'][] = 'rev_minor_edit = 0';
325  }
326 
327  $user = $this->getUser();
328  $queryInfo['conds'] = array_merge( $queryInfo['conds'], $this->getNamespaceCond() );
329 
330  // Paranoia: avoid brute force searches (T19342)
331  if ( !$permissionManager->userHasRight( $user, 'deletedhistory' ) ) {
332  $queryInfo['conds'][] = $this->mDb->bitAnd(
333  'rev_deleted', RevisionRecord::DELETED_USER
334  ) . ' = 0';
335  } elseif ( !$permissionManager->userHasAnyRight( $user, 'suppressrevision', 'viewsuppressed' ) ) {
336  $queryInfo['conds'][] = $this->mDb->bitAnd(
337  'rev_deleted', RevisionRecord::SUPPRESSED_USER
338  ) . ' != ' . RevisionRecord::SUPPRESSED_USER;
339  }
340 
341  // $this->getIndexField() must be in the result rows, as reallyDoQuery() tries to access it.
342  $indexField = $this->getIndexField();
343  if ( $indexField !== 'rev_timestamp' ) {
344  $queryInfo['fields'][] = $indexField;
345  }
346 
348  $queryInfo['tables'],
349  $queryInfo['fields'],
350  $queryInfo['conds'],
351  $queryInfo['join_conds'],
352  $queryInfo['options'],
353  $this->tagFilter
354  );
355 
356  $this->getHookRunner()->onContribsPager__getQueryInfo( $this, $queryInfo );
357 
358  return $queryInfo;
359  }
360 
361  protected function getNamespaceCond() {
362  if ( $this->namespace !== '' ) {
363  $selectedNS = $this->mDb->addQuotes( $this->namespace );
364  $eq_op = $this->nsInvert ? '!=' : '=';
365  $bool_op = $this->nsInvert ? 'AND' : 'OR';
366 
367  if ( !$this->associated ) {
368  return [ "page_namespace $eq_op $selectedNS" ];
369  }
370 
371  $associatedNS = $this->mDb->addQuotes(
372  MediaWikiServices::getInstance()->getNamespaceInfo()->getAssociated( $this->namespace )
373  );
374 
375  return [
376  "page_namespace $eq_op $selectedNS " .
377  $bool_op .
378  " page_namespace $eq_op $associatedNS"
379  ];
380  }
381 
382  return [];
383  }
384 
391  private function getIpRangeConds( $db, $ip ) {
392  // First make sure it is a valid range and they are not outside the CIDR limit
393  if ( !$this->isQueryableRange( $ip ) ) {
394  return false;
395  }
396 
397  list( $start, $end ) = IPUtils::parseRange( $ip );
398 
399  return 'ipc_hex BETWEEN ' . $db->addQuotes( $start ) . ' AND ' . $db->addQuotes( $end );
400  }
401 
409  public function isQueryableRange( $ipRange ) {
410  $limits = $this->getConfig()->get( 'RangeContributionsCIDRLimit' );
411 
412  $bits = IPUtils::parseCIDR( $ipRange )[1];
413  if (
414  ( $bits === false ) ||
415  ( IPUtils::isIPv4( $ipRange ) && $bits < $limits['IPv4'] ) ||
416  ( IPUtils::isIPv6( $ipRange ) && $bits < $limits['IPv6'] )
417  ) {
418  return false;
419  }
420 
421  return true;
422  }
423 
427  public function getIndexField() {
428  // The returned column is used for sorting and continuation, so we need to
429  // make sure to use the right denormalized column depending on which table is
430  // being targeted by the query to avoid bad query plans.
431  // See T200259, T204669, T220991, and T221380.
432  $target = $this->getTargetTable();
433  switch ( $target ) {
434  case 'revision':
435  return 'rev_timestamp';
436  case 'ip_changes':
437  return 'ipc_rev_timestamp';
438  case 'revision_actor_temp':
439  return 'revactor_timestamp';
440  default:
441  wfWarn(
442  __METHOD__ . ": Unknown value '$target' from " . static::class . '::getTargetTable()', 0
443  );
444  return 'rev_timestamp';
445  }
446  }
447 
451  public function getTagFilter() {
452  return $this->tagFilter;
453  }
454 
458  public function getTarget() {
459  return $this->target;
460  }
461 
465  public function isNewOnly() {
466  return $this->newOnly;
467  }
468 
472  public function getNamespace() {
473  return $this->namespace;
474  }
475 
479  protected function getExtraSortFields() {
480  // The returned columns are used for sorting, so we need to make sure
481  // to use the right denormalized column depending on which table is
482  // being targeted by the query to avoid bad query plans.
483  // See T200259, T204669, T220991, and T221380.
484  $target = $this->getTargetTable();
485  switch ( $target ) {
486  case 'revision':
487  return [ 'rev_id' ];
488  case 'ip_changes':
489  return [ 'ipc_rev_id' ];
490  case 'revision_actor_temp':
491  return [ 'revactor_rev' ];
492  default:
493  wfWarn(
494  __METHOD__ . ": Unknown value '$target' from " . static::class . '::getTargetTable()', 0
495  );
496  return [ 'rev_id' ];
497  }
498  }
499 
500  protected function doBatchLookups() {
501  # Do a link batch query
502  $this->mResult->seek( 0 );
503  $parentRevIds = [];
504  $this->mParentLens = [];
505  $batch = $this->linkBatchFactory->newLinkBatch();
506  $isIpRange = $this->isQueryableRange( $this->target );
507  # Give some pointers to make (last) links
508  foreach ( $this->mResult as $row ) {
509  if ( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
510  $parentRevIds[] = $row->rev_parent_id;
511  }
512  if ( isset( $row->rev_id ) ) {
513  $this->mParentLens[$row->rev_id] = $row->rev_len;
514  if ( $isIpRange ) {
515  // If this is an IP range, batch the IP's talk page
516  $batch->add( NS_USER_TALK, $row->rev_user_text );
517  }
518  $batch->add( $row->page_namespace, $row->page_title );
519  }
520  }
521  # Fetch rev_len for revisions not already scanned above
522  $this->mParentLens += MediaWikiServices::getInstance()
523  ->getRevisionStore()
524  ->getRevisionSizes( array_diff( $parentRevIds, array_keys( $this->mParentLens ) ) );
525  $batch->execute();
526  $this->mResult->seek( 0 );
527  }
528 
532  protected function getStartBody() {
533  return "<ul class=\"mw-contributions-list\">\n";
534  }
535 
539  protected function getEndBody() {
540  return "</ul>\n";
541  }
542 
553  public function tryToCreateValidRevision( $row, $title = null ) {
554  wfDeprecated( __METHOD__, '1.35' );
555  $potentialRevRecord = $this->tryCreatingRevisionRecord( $row, $title );
556  return $potentialRevRecord ? new Revision( $potentialRevRecord ) : null;
557  }
558 
569  public function tryCreatingRevisionRecord( $row, $title = null ) {
570  $revFactory = MediaWikiServices::getInstance()->getRevisionFactory();
571  /*
572  * There may be more than just revision rows. To make sure that we'll only be processing
573  * revisions here, let's _try_ to build a revision out of our row (without displaying
574  * notices though) and then trying to grab data from the built object. If we succeed,
575  * we're definitely dealing with revision data and we may proceed, if not, we'll leave it
576  * to extensions to subscribe to the hook to parse the row.
577  */
578  Wikimedia\suppressWarnings();
579  try {
580  $revRecord = $revFactory->newRevisionFromRow( $row, 0, $title );
581  return $revRecord->getId() ? $revRecord : null;
582  } catch ( Exception $e ) {
583  return null;
584  } finally {
585  Wikimedia\restoreWarnings();
586  }
587  }
588 
601  public function formatRow( $row ) {
602  $ret = '';
603  $classes = [];
604  $attribs = [];
605 
606  $linkRenderer = $this->getLinkRenderer();
607  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
608 
609  $page = null;
610  // Create a title for the revision if possible
611  // Rows from the hook may not include title information
612  if ( isset( $row->page_namespace ) && isset( $row->page_title ) ) {
613  $page = Title::newFromRow( $row );
614  }
615  $revRecord = $this->tryCreatingRevisionRecord( $row, $page );
616  if ( $revRecord ) {
617  $attribs['data-mw-revid'] = $revRecord->getId();
618 
619  $link = $linkRenderer->makeLink(
620  $page,
621  $page->getPrefixedText(),
622  [ 'class' => 'mw-contributions-title' ],
623  $page->isRedirect() ? [ 'redirect' => 'no' ] : []
624  );
625  # Mark current revisions
626  $topmarktext = '';
627  $user = $this->getUser();
628 
629  if ( $row->rev_id === $row->page_latest ) {
630  $topmarktext .= '<span class="mw-uctop">' . $this->messages['uctop'] . '</span>';
631  $classes[] = 'mw-contributions-current';
632  # Add rollback link
633  if ( !$row->page_is_new &&
634  $permissionManager->quickUserCan( 'rollback', $user, $page ) &&
635  $permissionManager->quickUserCan( 'edit', $user, $page )
636  ) {
637  $this->preventClickjacking();
638  $topmarktext .= ' ' . Linker::generateRollback(
639  $revRecord,
640  $this->getContext(),
641  [ 'noBrackets' ]
642  );
643  }
644  }
645  # Is there a visible previous revision?
646  if ( $revRecord->getParentId() !== 0 &&
647  RevisionRecord::userCanBitfield(
648  $revRecord->getVisibility(),
649  RevisionRecord::DELETED_TEXT,
650  $user
651  )
652  ) {
653  $difftext = $linkRenderer->makeKnownLink(
654  $page,
655  new HtmlArmor( $this->messages['diff'] ),
656  [ 'class' => 'mw-changeslist-diff' ],
657  [
658  'diff' => 'prev',
659  'oldid' => $row->rev_id
660  ]
661  );
662  } else {
663  $difftext = $this->messages['diff'];
664  }
665  $histlink = $linkRenderer->makeKnownLink(
666  $page,
667  new HtmlArmor( $this->messages['hist'] ),
668  [ 'class' => 'mw-changeslist-history' ],
669  [ 'action' => 'history' ]
670  );
671 
672  if ( $row->rev_parent_id === null ) {
673  // For some reason rev_parent_id isn't populated for this row.
674  // Its rumoured this is true on wikipedia for some revisions (T36922).
675  // Next best thing is to have the total number of bytes.
676  $chardiff = ' <span class="mw-changeslist-separator"></span> ';
677  $chardiff .= Linker::formatRevisionSize( $row->rev_len );
678  $chardiff .= ' <span class="mw-changeslist-separator"></span> ';
679  } else {
680  $parentLen = 0;
681  if ( isset( $this->mParentLens[$row->rev_parent_id] ) ) {
682  $parentLen = $this->mParentLens[$row->rev_parent_id];
683  }
684 
685  $chardiff = ' <span class="mw-changeslist-separator"></span> ';
687  $parentLen,
688  $row->rev_len,
689  $this->getContext()
690  );
691  $chardiff .= ' <span class="mw-changeslist-separator"></span> ';
692  }
693 
694  $lang = $this->getLanguage();
695  $comment = $lang->getDirMark() . Linker::revComment( $revRecord, false, true, false );
696  $d = ChangesList::revDateLink( $revRecord, $user, $lang, $page );
697 
698  # When querying for an IP range, we want to always show user and user talk links.
699  $userlink = '';
700  $revUser = $revRecord->getUser();
701  $revUserId = $revUser ? $revUser->getId() : 0;
702  $revUserText = $revUser ? $revUser->getName() : '';
703  if ( $this->isQueryableRange( $this->target ) ) {
704  $userlink = ' <span class="mw-changeslist-separator"></span> '
705  . $lang->getDirMark()
706  . Linker::userLink( $revUserId, $revUserText );
707  $userlink .= ' ' . $this->msg( 'parentheses' )->rawParams(
708  Linker::userTalkLink( $revUserId, $revUserText ) )->escaped() . ' ';
709  }
710 
711  $flags = [];
712  if ( $revRecord->getParentId() === 0 ) {
713  $flags[] = ChangesList::flag( 'newpage' );
714  }
715 
716  if ( $revRecord->isMinor() ) {
717  $flags[] = ChangesList::flag( 'minor' );
718  }
719 
720  $del = Linker::getRevDeleteLink( $user, $revRecord, $page );
721  if ( $del !== '' ) {
722  $del .= ' ';
723  }
724 
725  // While it might be tempting to use a list here
726  // this would result in clutter and slows down navigating the content
727  // in assistive technology.
728  // See https://phabricator.wikimedia.org/T205581#4734812
729  $diffHistLinks = Html::rawElement( 'span',
730  [ 'class' => 'mw-changeslist-links' ],
731  // The spans are needed to ensure the dividing '|' elements are not
732  // themselves styled as links.
733  Html::rawElement( 'span', [], $difftext ) .
734  ' ' . // Space needed for separating two words.
735  Html::rawElement( 'span', [], $histlink )
736  );
737 
738  # Tags, if any.
739  list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow(
740  $row->ts_tags,
741  'contributions',
742  $this->getContext()
743  );
744  $classes = array_merge( $classes, $newClasses );
745 
746  $this->getHookRunner()->onSpecialContributions__formatRow__flags(
747  $this->getContext(), $row, $flags );
748 
749  $templateParams = [
750  'del' => $del,
751  'timestamp' => $d,
752  'diffHistLinks' => $diffHistLinks,
753  'charDifference' => $chardiff,
754  'flags' => $flags,
755  'articleLink' => $link,
756  'userlink' => $userlink,
757  'logText' => $comment,
758  'topmarktext' => $topmarktext,
759  'tagSummary' => $tagSummary,
760  ];
761 
762  # Denote if username is redacted for this edit
763  if ( $revRecord->isDeleted( RevisionRecord::DELETED_USER ) ) {
764  $templateParams['rev-deleted-user-contribs'] =
765  $this->msg( 'rev-deleted-user-contribs' )->escaped();
766  }
767 
768  $ret = $this->templateParser->processTemplate(
769  'SpecialContributionsLine',
770  $templateParams
771  );
772  }
773 
774  // Let extensions add data
775  $this->getHookRunner()->onContributionsLineEnding( $this, $ret, $row, $classes, $attribs );
776  $attribs = array_filter( $attribs,
777  [ Sanitizer::class, 'isReservedDataAttribute' ],
778  ARRAY_FILTER_USE_KEY
779  );
780 
781  // TODO: Handle exceptions in the catch block above. Do any extensions rely on
782  // receiving empty rows?
783 
784  if ( $classes === [] && $attribs === [] && $ret === '' ) {
785  wfDebug( "Dropping Special:Contribution row that could not be formatted" );
786  return "<!-- Could not format Special:Contribution row. -->\n";
787  }
788  $attribs['class'] = $classes;
789 
790  // FIXME: The signature of the ContributionsLineEnding hook makes it
791  // very awkward to move this LI wrapper into the template.
792  return Html::rawElement( 'li', $attribs, $ret ) . "\n";
793  }
794 
799  protected function getSqlComment() {
800  if ( $this->namespace || $this->deletedOnly ) {
801  // potentially slow, see CR r58153
802  return 'contributions page filtered for namespace or RevisionDeleted edits';
803  } else {
804  return 'contributions page unfiltered';
805  }
806  }
807 
808  protected function preventClickjacking() {
809  $this->preventClickjacking = true;
810  }
811 
815  public function getPreventClickjacking() {
816  return $this->preventClickjacking;
817  }
818 
825  public static function processDateFilter( array $opts ) {
826  $start = $opts['start'] ?? '';
827  $end = $opts['end'] ?? '';
828  $year = $opts['year'] ?? '';
829  $month = $opts['month'] ?? '';
830 
831  if ( $start !== '' && $end !== '' && $start > $end ) {
832  $temp = $start;
833  $start = $end;
834  $end = $temp;
835  }
836 
837  // If year/month legacy filtering options are set, convert them to display the new stamp
838  if ( $year !== '' || $month !== '' ) {
839  // Reuse getDateCond logic, but subtract a day because
840  // the endpoints of our date range appear inclusive
841  // but the internal end offsets are always exclusive
842  $legacyTimestamp = ReverseChronologicalPager::getOffsetDate( $year, $month );
843  $legacyDateTime = new DateTime( $legacyTimestamp->getTimestamp( TS_ISO_8601 ) );
844  $legacyDateTime = $legacyDateTime->modify( '-1 day' );
845 
846  // Clear the new timestamp range options if used and
847  // replace with the converted legacy timestamp
848  $start = '';
849  $end = $legacyDateTime->format( 'Y-m-d' );
850  }
851 
852  $opts['start'] = $start;
853  $opts['end'] = $end;
854 
855  return $opts;
856  }
857 }
ReverseChronologicalPager\getOffsetDate
static getOffsetDate( $year, $month, $day=-1)
Core logic of determining the mOffset timestamp such that we can get all items with a timestamp up to...
Definition: ReverseChronologicalPager.php:129
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:37
ContribsPager\getNamespace
getNamespace()
Definition: ContribsPager.php:472
HtmlArmor
Marks HTML that shouldn't be escaped.
Definition: HtmlArmor.php:30
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
Linker\userTalkLink
static userTalkLink( $userId, $userText)
Definition: Linker.php:1035
Linker\userLink
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:896
ContribsPager\$topOnly
bool $topOnly
Set to true to show only latest (a.k.a.
Definition: ContribsPager.php:76
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:163
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
MediaWiki\Linker\LinkRenderer
Class that generates HTML links for pages.
Definition: LinkRenderer.php:41
getUser
getUser()
ContribsPager\getDefaultQuery
getDefaultQuery()
Get an array of query parameters that should be put into self-links.
Definition: ContribsPager.php:161
Linker\revComment
static revComment( $rev, $local=false, $isPublic=false, $useParentheses=true)
Wrap and format the given revision's comment block, if the current user is allowed to view it.
Definition: Linker.php:1605
Linker\getRevDeleteLink
static getRevDeleteLink(User $user, $rev, LinkTarget $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2195
ContribsPager\getSqlComment
getSqlComment()
Overwrite Pager function and return a helpful comment.
Definition: ContribsPager.php:799
ContribsPager\formatRow
formatRow( $row)
Generates each row in the contributions list.
Definition: ContribsPager.php:601
User\newFromName
static newFromName( $name, $validate='valid')
Definition: User.php:545
IndexPager\$linkRenderer
LinkRenderer $linkRenderer
Definition: IndexPager.php:167
ContribsPager\reallyDoQuery
reallyDoQuery( $offset, $limit, $order)
This method basically executes the exact same code as the parent class, though with a hook added,...
Definition: ContribsPager.php:190
ContribsPager\tryCreatingRevisionRecord
tryCreatingRevisionRecord( $row, $title=null)
Check whether the revision associated is valid for formatting.
Definition: ContribsPager.php:569
ContribsPager\tryToCreateValidRevision
tryToCreateValidRevision( $row, $title=null)
Check whether the revision associated is valid for formatting.
Definition: ContribsPager.php:553
Wikimedia\Rdbms\FakeResultWrapper
Overloads the relevant methods of the real ResultsWrapper so it doesn't go anywhere near an actual da...
Definition: FakeResultWrapper.php:11
ContribsPager\$target
string $target
User name, or a string describing an IP address range.
Definition: ContribsPager.php:45
getContext
getContext()
ContribsPager\doBatchLookups
doBatchLookups()
Called from getBody(), before getStartBody() is called and after doQuery() was called.
Definition: ContribsPager.php:500
$revQuery
$revQuery
Definition: testCompression.php:56
ActorMigration\newMigration
static newMigration()
Static constructor.
Definition: ActorMigration.php:156
ContribsPager\processDateFilter
static processDateFilter(array $opts)
Set up date filter options, given request data.
Definition: ContribsPager.php:825
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
Revision
Definition: Revision.php:40
ContribsPager\getPreventClickjacking
getPreventClickjacking()
Definition: ContribsPager.php:815
ChangeTags\modifyDisplayQuery
static modifyDisplayQuery(&$tables, &$fields, &$conds, &$join_conds, &$options, $filter_tag='')
Applies all tags-related changes to a query.
Definition: ChangeTags.php:773
ContribsPager\$linkBatchFactory
LinkBatchFactory $linkBatchFactory
Definition: ContribsPager.php:107
ContribsPager\getNamespaceCond
getNamespaceCond()
Definition: ContribsPager.php:361
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1027
Linker\generateRollback
static generateRollback( $rev, IContextSource $context=null, $options=[ 'verify'])
Generate a rollback link for a given revision.
Definition: Linker.php:1861
ContribsPager\getTarget
getTarget()
Definition: ContribsPager.php:458
Wikimedia\Rdbms\IResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: IResultWrapper.php:24
ContribsPager\getStartBody
getStartBody()
Definition: ContribsPager.php:532
Title\newFromRow
static newFromRow( $row)
Make a Title object from a DB row.
Definition: Title.php:523
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2459
RangeChronologicalPager\getDateRangeCond
getDateRangeCond( $startStamp, $endStamp)
Set and return a date range condition using timestamps provided by the user.
Definition: RangeChronologicalPager.php:46
MediaWiki\Cache\LinkBatchFactory
Definition: LinkBatchFactory.php:38
ContribsPager\$deletedOnly
bool $deletedOnly
Set to true to show only deleted revisions.
Definition: ContribsPager.php:71
ChangesList\flag
static flag( $flag, IContextSource $context=null)
Make an "<abbr>" element for a given change flag.
Definition: ChangesList.php:269
$title
$title
Definition: testCompression.php:38
ContribsPager\isNewOnly
isNewOnly()
Definition: ContribsPager.php:465
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:910
RangeChronologicalPager
Pager for filtering by a range of dates.
Definition: RangeChronologicalPager.php:28
ContextSource\msg
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:187
ContribsPager
Pager for Special:Contributions.
Definition: ContribsPager.php:35
ContribsPager\isQueryableRange
isQueryableRange( $ipRange)
Is the given IP a range and within the CIDR limit?
Definition: ContribsPager.php:409
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:72
ContribsPager\__construct
__construct(IContextSource $context, array $options, LinkRenderer $linkRenderer=null, LinkBatchFactory $linkBatchFactory=null)
Definition: ContribsPager.php:109
ContribsPager\getNavigationBar
getNavigationBar()
Wrap the navigation bar in a p element with identifying class.
Definition: ContribsPager.php:175
ContribsPager\$preventClickjacking
$preventClickjacking
Definition: ContribsPager.php:94
ContribsPager\getExtraSortFields
getExtraSortFields()
Definition: ContribsPager.php:479
ContribsPager\$templateParser
TemplateParser $templateParser
Definition: ContribsPager.php:104
Linker\formatRevisionSize
static formatRevisionSize( $size)
Definition: Linker.php:1650
ChangesList\showCharacterDifference
static showCharacterDifference( $old, $new, IContextSource $context=null)
Show formatted char difference.
Definition: ChangesList.php:333
ContribsPager\$tagFilter
string false $tagFilter
Name of tag to filter, or false to ignore tags.
Definition: ContribsPager.php:55
RangeChronologicalPager\buildQueryInfo
buildQueryInfo( $offset, $limit, $order)
Build variables to use by the database wrapper.
Definition: RangeChronologicalPager.php:107
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:55
ContribsPager\$newOnly
bool $newOnly
Set to true to show only new pages.
Definition: ContribsPager.php:81
ContribsPager\$hideMinor
bool $hideMinor
Set to true to hide edits marked as minor by the user.
Definition: ContribsPager.php:86
ContribsPager\$associated
bool $associated
Set to true to show both the subject and talk namespace, no matter which got selected.
Definition: ContribsPager.php:66
ContribsPager\getTargetTable
getTargetTable()
Return the table targeted for ordering and continuation.
Definition: ContribsPager.php:262
ContribsPager\$revisionsOnly
bool $revisionsOnly
Set to true to only include mediawiki revisions.
Definition: ContribsPager.php:92
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:212
ContribsPager\getIpRangeConds
getIpRangeConds( $db, $ip)
Get SQL conditions for an IP range, if applicable.
Definition: ContribsPager.php:391
wfWarn
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
Definition: GlobalFunctions.php:1074
ContribsPager\getEndBody
getEndBody()
Definition: ContribsPager.php:539
ContribsPager\getQueryInfo
getQueryInfo()
Provides all parameters needed for the main paged query.
Definition: ContribsPager.php:278
ContribsPager\$messages
string[] $messages
Local cache for escaped messages.
Definition: ContribsPager.php:40
ContribsPager\$nsInvert
bool $nsInvert
Set to true to invert the namespace selection.
Definition: ContribsPager.php:60
ChangesList\revDateLink
static revDateLink(RevisionRecord $rev, User $user, Language $lang, $title=null)
Render the date and time of a revision in the current user language based on whether the user is able...
Definition: ChangesList.php:425
ContribsPager\$namespace
string int $namespace
A single namespace number, or an empty string for all namespaces.
Definition: ContribsPager.php:50
ContribsPager\preventClickjacking
preventClickjacking()
Definition: ContribsPager.php:808
TemplateParser
Definition: TemplateParser.php:27
ContribsPager\getIndexField
getIndexField()
Definition: ContribsPager.php:427
ChangeTags\formatSummaryRow
static formatSummaryRow( $tags, $page, IContextSource $context=null)
Creates HTML for the given tags.
Definition: ChangeTags.php:114
ContribsPager\$mParentLens
array $mParentLens
Definition: ContribsPager.php:99
ContribsPager\getTagFilter
getTagFilter()
Definition: ContribsPager.php:451