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