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 
87  private $preventClickjacking = false;
88 
90  private $mDbSecondary;
91 
95  private $mParentLens;
96 
101 
102  public function __construct( IContextSource $context, array $options,
104  ) {
105  // Set ->target before calling parent::__construct() so
106  // parent can call $this->getIndexField() and get the right result. Set
107  // the rest too just to keep things simple.
108  $this->target = $options['target'] ?? '';
109  $this->namespace = $options['namespace'] ?? '';
110  $this->tagFilter = $options['tagfilter'] ?? false;
111  $this->nsInvert = $options['nsInvert'] ?? false;
112  $this->associated = $options['associated'] ?? false;
113 
114  $this->deletedOnly = !empty( $options['deletedOnly'] );
115  $this->topOnly = !empty( $options['topOnly'] );
116  $this->newOnly = !empty( $options['newOnly'] );
117  $this->hideMinor = !empty( $options['hideMinor'] );
118 
119  parent::__construct( $context, $linkRenderer );
120 
121  $msgs = [
122  'diff',
123  'hist',
124  'pipe-separator',
125  'uctop'
126  ];
127 
128  foreach ( $msgs as $msg ) {
129  $this->messages[$msg] = $this->msg( $msg )->escaped();
130  }
131 
132  // Date filtering: use timestamp if available
133  $startTimestamp = '';
134  $endTimestamp = '';
135  if ( isset( $options['start'] ) && $options['start'] ) {
136  $startTimestamp = $options['start'] . ' 00:00:00';
137  }
138  if ( isset( $options['end'] ) && $options['end'] ) {
139  $endTimestamp = $options['end'] . ' 23:59:59';
140  }
141  $this->getDateRangeCond( $startTimestamp, $endTimestamp );
142 
143  // Most of this code will use the 'contributions' group DB, which can map to replica DBs
144  // with extra user based indexes or partioning by user. The additional metadata
145  // queries should use a regular replica DB since the lookup pattern is not all by user.
146  $this->mDbSecondary = wfGetDB( DB_REPLICA ); // any random replica DB
147  $this->mDb = wfGetDB( DB_REPLICA, 'contributions' );
148  $this->templateParser = new TemplateParser();
149  }
150 
151  function getDefaultQuery() {
152  $query = parent::getDefaultQuery();
153  $query['target'] = $this->target;
154 
155  return $query;
156  }
157 
165  function getNavigationBar() {
166  return Html::rawElement( 'p', [ 'class' => 'mw-pager-navigation-bar' ],
167  parent::getNavigationBar()
168  );
169  }
170 
180  function reallyDoQuery( $offset, $limit, $order ) {
181  list( $tables, $fields, $conds, $fname, $options, $join_conds ) = $this->buildQueryInfo(
182  $offset,
183  $limit,
184  $order
185  );
186 
187  /*
188  * This hook will allow extensions to add in additional queries, so they can get their data
189  * in My Contributions as well. Extensions should append their results to the $data array.
190  *
191  * Extension queries have to implement the navbar requirement as well. They should
192  * - have a column aliased as $pager->getIndexField()
193  * - have LIMIT set
194  * - have a WHERE-clause that compares the $pager->getIndexField()-equivalent column to the offset
195  * - have the ORDER BY specified based upon the details provided by the navbar
196  *
197  * See includes/Pager.php buildQueryInfo() method on how to build LIMIT, WHERE & ORDER BY
198  *
199  * &$data: an array of results of all contribs queries
200  * $pager: the ContribsPager object hooked into
201  * $offset: see phpdoc above
202  * $limit: see phpdoc above
203  * $descending: see phpdoc above
204  */
205  $data = [ $this->mDb->select(
206  $tables, $fields, $conds, $fname, $options, $join_conds
207  ) ];
208  Hooks::run(
209  'ContribsPager::reallyDoQuery',
210  [ &$data, $this, $offset, $limit, $order ]
211  );
212 
213  $result = [];
214 
215  // loop all results and collect them in an array
216  foreach ( $data as $query ) {
217  foreach ( $query as $i => $row ) {
218  // use index column as key, allowing us to easily sort in PHP
219  $result[$row->{$this->getIndexField()} . "-$i"] = $row;
220  }
221  }
222 
223  // sort results
224  if ( $order === self::QUERY_ASCENDING ) {
225  ksort( $result );
226  } else {
227  krsort( $result );
228  }
229 
230  // enforce limit
231  $result = array_slice( $result, 0, $limit );
232 
233  // get rid of array keys
234  $result = array_values( $result );
235 
236  return new FakeResultWrapper( $result );
237  }
238 
248  private function getTargetTable() {
249  $user = User::newFromName( $this->target, false );
250  $ipRangeConds = $user->isAnon() ? $this->getIpRangeConds( $this->mDb, $this->target ) : null;
251  if ( $ipRangeConds ) {
252  return 'ip_changes';
253  } else {
254  $conds = ActorMigration::newMigration()->getWhere( $this->mDb, 'rev_user', $user );
255  if ( isset( $conds['orconds']['actor'] ) ) {
256  // @todo: This will need changing when revision_actor_temp goes away
257  return 'revision_actor_temp';
258  }
259  }
260 
261  return 'revision';
262  }
263 
264  function getQueryInfo() {
265  $revQuery = Revision::getQueryInfo( [ 'page', 'user' ] );
266  $queryInfo = [
267  'tables' => $revQuery['tables'],
268  'fields' => array_merge( $revQuery['fields'], [ 'page_is_new' ] ),
269  'conds' => [],
270  'options' => [],
271  'join_conds' => $revQuery['joins'],
272  ];
273  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
274 
275  // WARNING: Keep this in sync with getTargetTable()!
276  $user = User::newFromName( $this->target, false );
277  $ipRangeConds = $user->isAnon() ? $this->getIpRangeConds( $this->mDb, $this->target ) : null;
278  if ( $ipRangeConds ) {
279  $queryInfo['tables'][] = 'ip_changes';
280  $queryInfo['join_conds']['ip_changes'] = [
281  'LEFT JOIN', [ 'ipc_rev_id = rev_id' ]
282  ];
283  $queryInfo['conds'][] = $ipRangeConds;
284  } else {
285  // tables and joins are already handled by Revision::getQueryInfo()
286  $conds = ActorMigration::newMigration()->getWhere( $this->mDb, 'rev_user', $user );
287  $queryInfo['conds'][] = $conds['conds'];
288  // Force the appropriate index to avoid bad query plans (T189026)
289  if ( isset( $conds['orconds']['actor'] ) ) {
290  // @todo: This will need changing when revision_actor_temp goes away
291  $queryInfo['options']['USE INDEX']['temp_rev_user'] = 'actor_timestamp';
292  } else {
293  $queryInfo['options']['USE INDEX']['revision'] =
294  isset( $conds['orconds']['userid'] ) ? 'user_timestamp' : 'usertext_timestamp';
295  }
296  }
297 
298  if ( $this->deletedOnly ) {
299  $queryInfo['conds'][] = 'rev_deleted != 0';
300  }
301 
302  if ( $this->topOnly ) {
303  $queryInfo['conds'][] = 'rev_id = page_latest';
304  }
305 
306  if ( $this->newOnly ) {
307  $queryInfo['conds'][] = 'rev_parent_id = 0';
308  }
309 
310  if ( $this->hideMinor ) {
311  $queryInfo['conds'][] = 'rev_minor_edit = 0';
312  }
313 
314  $user = $this->getUser();
315  $queryInfo['conds'] = array_merge( $queryInfo['conds'], $this->getNamespaceCond() );
316 
317  // Paranoia: avoid brute force searches (T19342)
318  if ( !$permissionManager->userHasRight( $user, 'deletedhistory' ) ) {
319  $queryInfo['conds'][] = $this->mDb->bitAnd(
320  'rev_deleted', RevisionRecord::DELETED_USER
321  ) . ' = 0';
322  } elseif ( !$permissionManager->userHasAnyRight( $user, 'suppressrevision', 'viewsuppressed' ) ) {
323  $queryInfo['conds'][] = $this->mDb->bitAnd(
324  'rev_deleted', RevisionRecord::SUPPRESSED_USER
325  ) . ' != ' . RevisionRecord::SUPPRESSED_USER;
326  }
327 
328  // $this->getIndexField() must be in the result rows, as reallyDoQuery() tries to access it.
329  $indexField = $this->getIndexField();
330  if ( $indexField !== 'rev_timestamp' ) {
331  $queryInfo['fields'][] = $indexField;
332  }
333 
335  $queryInfo['tables'],
336  $queryInfo['fields'],
337  $queryInfo['conds'],
338  $queryInfo['join_conds'],
339  $queryInfo['options'],
340  $this->tagFilter
341  );
342 
343  // Avoid PHP 7.1 warning from passing $this by reference
344  $pager = $this;
345  Hooks::run( 'ContribsPager::getQueryInfo', [ &$pager, &$queryInfo ] );
346 
347  return $queryInfo;
348  }
349 
350  function getNamespaceCond() {
351  if ( $this->namespace !== '' ) {
352  $selectedNS = $this->mDb->addQuotes( $this->namespace );
353  $eq_op = $this->nsInvert ? '!=' : '=';
354  $bool_op = $this->nsInvert ? 'AND' : 'OR';
355 
356  if ( !$this->associated ) {
357  return [ "page_namespace $eq_op $selectedNS" ];
358  }
359 
360  $associatedNS = $this->mDb->addQuotes(
361  MediaWikiServices::getInstance()->getNamespaceInfo()->getAssociated( $this->namespace )
362  );
363 
364  return [
365  "page_namespace $eq_op $selectedNS " .
366  $bool_op .
367  " page_namespace $eq_op $associatedNS"
368  ];
369  }
370 
371  return [];
372  }
373 
380  private function getIpRangeConds( $db, $ip ) {
381  // First make sure it is a valid range and they are not outside the CIDR limit
382  if ( !$this->isQueryableRange( $ip ) ) {
383  return false;
384  }
385 
386  list( $start, $end ) = IPUtils::parseRange( $ip );
387 
388  return 'ipc_hex BETWEEN ' . $db->addQuotes( $start ) . ' AND ' . $db->addQuotes( $end );
389  }
390 
398  public function isQueryableRange( $ipRange ) {
399  $limits = $this->getConfig()->get( 'RangeContributionsCIDRLimit' );
400 
401  $bits = IPUtils::parseCIDR( $ipRange )[1];
402  if (
403  ( $bits === false ) ||
404  ( IPUtils::isIPv4( $ipRange ) && $bits < $limits['IPv4'] ) ||
405  ( IPUtils::isIPv6( $ipRange ) && $bits < $limits['IPv6'] )
406  ) {
407  return false;
408  }
409 
410  return true;
411  }
412 
416  public function getIndexField() {
417  // The returned column is used for sorting and continuation, so we need to
418  // make sure to use the right denormalized column depending on which table is
419  // being targeted by the query to avoid bad query plans.
420  // See T200259, T204669, T220991, and T221380.
421  $target = $this->getTargetTable();
422  switch ( $target ) {
423  case 'revision':
424  return 'rev_timestamp';
425  case 'ip_changes':
426  return 'ipc_rev_timestamp';
427  case 'revision_actor_temp':
428  return 'revactor_timestamp';
429  default:
430  wfWarn(
431  __METHOD__ . ": Unknown value '$target' from " . static::class . '::getTargetTable()', 0
432  );
433  return 'rev_timestamp';
434  }
435  }
436 
440  public function getTagFilter() {
441  return $this->tagFilter;
442  }
443 
447  public function getTarget() {
448  return $this->target;
449  }
450 
454  public function isNewOnly() {
455  return $this->newOnly;
456  }
457 
461  public function getNamespace() {
462  return $this->namespace;
463  }
464 
468  protected function getExtraSortFields() {
469  // The returned columns are used for sorting, so we need to make sure
470  // to use the right denormalized column depending on which table is
471  // being targeted by the query to avoid bad query plans.
472  // See T200259, T204669, T220991, and T221380.
473  $target = $this->getTargetTable();
474  switch ( $target ) {
475  case 'revision':
476  return [ 'rev_id' ];
477  case 'ip_changes':
478  return [ 'ipc_rev_id' ];
479  case 'revision_actor_temp':
480  return [ 'revactor_rev' ];
481  default:
482  wfWarn(
483  __METHOD__ . ": Unknown value '$target' from " . static::class . '::getTargetTable()', 0
484  );
485  return [ 'rev_id' ];
486  }
487  }
488 
489  protected function doBatchLookups() {
490  # Do a link batch query
491  $this->mResult->seek( 0 );
492  $parentRevIds = [];
493  $this->mParentLens = [];
494  $batch = new LinkBatch();
495  $isIpRange = $this->isQueryableRange( $this->target );
496  # Give some pointers to make (last) links
497  foreach ( $this->mResult as $row ) {
498  if ( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
499  $parentRevIds[] = $row->rev_parent_id;
500  }
501  if ( isset( $row->rev_id ) ) {
502  $this->mParentLens[$row->rev_id] = $row->rev_len;
503  if ( $isIpRange ) {
504  // If this is an IP range, batch the IP's talk page
505  $batch->add( NS_USER_TALK, $row->rev_user_text );
506  }
507  $batch->add( $row->page_namespace, $row->page_title );
508  }
509  }
510  # Fetch rev_len for revisions not already scanned above
511  $this->mParentLens += Revision::getParentLengths(
512  $this->mDbSecondary,
513  array_diff( $parentRevIds, array_keys( $this->mParentLens ) )
514  );
515  $batch->execute();
516  $this->mResult->seek( 0 );
517  }
518 
522  protected function getStartBody() {
523  return "<ul class=\"mw-contributions-list\">\n";
524  }
525 
529  protected function getEndBody() {
530  return "</ul>\n";
531  }
532 
541  public function tryToCreateValidRevision( $row, $title = null ) {
542  /*
543  * There may be more than just revision rows. To make sure that we'll only be processing
544  * revisions here, let's _try_ to build a revision out of our row (without displaying
545  * notices though) and then trying to grab data from the built object. If we succeed,
546  * we're definitely dealing with revision data and we may proceed, if not, we'll leave it
547  * to extensions to subscribe to the hook to parse the row.
548  */
549  Wikimedia\suppressWarnings();
550  try {
551  $rev = new Revision( $row, 0, $title );
552  $validRevision = (bool)$rev->getId();
553  } catch ( Exception $e ) {
554  $validRevision = false;
555  }
556  Wikimedia\restoreWarnings();
557  return $validRevision ? $rev : null;
558  }
559 
572  function formatRow( $row ) {
573  $ret = '';
574  $classes = [];
575  $attribs = [];
576 
577  $linkRenderer = $this->getLinkRenderer();
578  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
579 
580  $page = null;
581  // Create a title for the revision if possible
582  // Rows from the hook may not include title information
583  if ( isset( $row->page_namespace ) && isset( $row->page_title ) ) {
584  $page = Title::newFromRow( $row );
585  }
586  $rev = $this->tryToCreateValidRevision( $row, $page );
587  if ( $rev ) {
588  $attribs['data-mw-revid'] = $rev->getId();
589 
590  $link = $linkRenderer->makeLink(
591  $page,
592  $page->getPrefixedText(),
593  [ 'class' => 'mw-contributions-title' ],
594  $page->isRedirect() ? [ 'redirect' => 'no' ] : []
595  );
596  # Mark current revisions
597  $topmarktext = '';
598  $user = $this->getUser();
599 
600  if ( $row->rev_id === $row->page_latest ) {
601  $topmarktext .= '<span class="mw-uctop">' . $this->messages['uctop'] . '</span>';
602  $classes[] = 'mw-contributions-current';
603  # Add rollback link
604  if ( !$row->page_is_new &&
605  $permissionManager->quickUserCan( 'rollback', $user, $page ) &&
606  $permissionManager->quickUserCan( 'edit', $user, $page )
607  ) {
608  $this->preventClickjacking();
609  $topmarktext .= ' ' . Linker::generateRollback( $rev, $this->getContext(),
610  [ 'noBrackets' ] );
611  }
612  }
613  # Is there a visible previous revision?
614  if ( $rev->userCan( RevisionRecord::DELETED_TEXT, $user ) && $rev->getParentId() !== 0 ) {
615  $difftext = $linkRenderer->makeKnownLink(
616  $page,
617  new HtmlArmor( $this->messages['diff'] ),
618  [ 'class' => 'mw-changeslist-diff' ],
619  [
620  'diff' => 'prev',
621  'oldid' => $row->rev_id
622  ]
623  );
624  } else {
625  $difftext = $this->messages['diff'];
626  }
627  $histlink = $linkRenderer->makeKnownLink(
628  $page,
629  new HtmlArmor( $this->messages['hist'] ),
630  [ 'class' => 'mw-changeslist-history' ],
631  [ 'action' => 'history' ]
632  );
633 
634  if ( $row->rev_parent_id === null ) {
635  // For some reason rev_parent_id isn't populated for this row.
636  // Its rumoured this is true on wikipedia for some revisions (T36922).
637  // Next best thing is to have the total number of bytes.
638  $chardiff = ' <span class="mw-changeslist-separator"></span> ';
639  $chardiff .= Linker::formatRevisionSize( $row->rev_len );
640  $chardiff .= ' <span class="mw-changeslist-separator"></span> ';
641  } else {
642  $parentLen = 0;
643  if ( isset( $this->mParentLens[$row->rev_parent_id] ) ) {
644  $parentLen = $this->mParentLens[$row->rev_parent_id];
645  }
646 
647  $chardiff = ' <span class="mw-changeslist-separator"></span> ';
649  $parentLen,
650  $row->rev_len,
651  $this->getContext()
652  );
653  $chardiff .= ' <span class="mw-changeslist-separator"></span> ';
654  }
655 
656  $lang = $this->getLanguage();
657  $comment = $lang->getDirMark() . Linker::revComment( $rev, false, true, false );
658  $d = ChangesList::revDateLink( $rev, $user, $lang, $page );
659 
660  # When querying for an IP range, we want to always show user and user talk links.
661  $userlink = '';
662  if ( $this->isQueryableRange( $this->target ) ) {
663  $userlink = ' <span class="mw-changeslist-separator"></span> '
664  . $lang->getDirMark()
665  . Linker::userLink( $rev->getUser(), $rev->getUserText() );
666  $userlink .= ' ' . $this->msg( 'parentheses' )->rawParams(
667  Linker::userTalkLink( $rev->getUser(), $rev->getUserText() ) )->escaped() . ' ';
668  }
669 
670  $flags = [];
671  if ( $rev->getParentId() === 0 ) {
672  $flags[] = ChangesList::flag( 'newpage' );
673  }
674 
675  if ( $rev->isMinor() ) {
676  $flags[] = ChangesList::flag( 'minor' );
677  }
678 
679  $del = Linker::getRevDeleteLink( $user, $rev, $page );
680  if ( $del !== '' ) {
681  $del .= ' ';
682  }
683 
684  // While it might be tempting to use a list here
685  // this would result in clutter and slows down navigating the content
686  // in assistive technology.
687  // See https://phabricator.wikimedia.org/T205581#4734812
688  $diffHistLinks = Html::rawElement( 'span',
689  [ 'class' => 'mw-changeslist-links' ],
690  // The spans are needed to ensure the dividing '|' elements are not
691  // themselves styled as links.
692  Html::rawElement( 'span', [], $difftext ) .
693  ' ' . // Space needed for separating two words.
694  Html::rawElement( 'span', [], $histlink )
695  );
696 
697  # Tags, if any.
698  list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow(
699  $row->ts_tags,
700  'contributions',
701  $this->getContext()
702  );
703  $classes = array_merge( $classes, $newClasses );
704 
705  Hooks::run( 'SpecialContributions::formatRow::flags', [ $this->getContext(), $row, &$flags ] );
706 
707  $templateParams = [
708  'del' => $del,
709  'timestamp' => $d,
710  'diffHistLinks' => $diffHistLinks,
711  'charDifference' => $chardiff,
712  'flags' => $flags,
713  'articleLink' => $link,
714  'userlink' => $userlink,
715  'logText' => $comment,
716  'topmarktext' => $topmarktext,
717  'tagSummary' => $tagSummary,
718  ];
719 
720  # Denote if username is redacted for this edit
721  if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) ) {
722  $templateParams['rev-deleted-user-contribs'] =
723  $this->msg( 'rev-deleted-user-contribs' )->escaped();
724  }
725 
726  $ret = $this->templateParser->processTemplate(
727  'SpecialContributionsLine',
728  $templateParams
729  );
730  }
731 
732  // Let extensions add data
733  Hooks::run( 'ContributionsLineEnding', [ $this, &$ret, $row, &$classes, &$attribs ] );
734  $attribs = array_filter( $attribs,
735  [ Sanitizer::class, 'isReservedDataAttribute' ],
736  ARRAY_FILTER_USE_KEY
737  );
738 
739  // TODO: Handle exceptions in the catch block above. Do any extensions rely on
740  // receiving empty rows?
741 
742  if ( $classes === [] && $attribs === [] && $ret === '' ) {
743  wfDebug( "Dropping Special:Contribution row that could not be formatted\n" );
744  return "<!-- Could not format Special:Contribution row. -->\n";
745  }
746  $attribs['class'] = $classes;
747 
748  // FIXME: The signature of the ContributionsLineEnding hook makes it
749  // very awkward to move this LI wrapper into the template.
750  return Html::rawElement( 'li', $attribs, $ret ) . "\n";
751  }
752 
757  function getSqlComment() {
758  if ( $this->namespace || $this->deletedOnly ) {
759  // potentially slow, see CR r58153
760  return 'contributions page filtered for namespace or RevisionDeleted edits';
761  } else {
762  return 'contributions page unfiltered';
763  }
764  }
765 
766  protected function preventClickjacking() {
767  $this->preventClickjacking = true;
768  }
769 
773  public function getPreventClickjacking() {
774  return $this->preventClickjacking;
775  }
776 
783  public static function processDateFilter( array $opts ) {
784  $start = $opts['start'] ?? '';
785  $end = $opts['end'] ?? '';
786  $year = $opts['year'] ?? '';
787  $month = $opts['month'] ?? '';
788 
789  if ( $start !== '' && $end !== '' && $start > $end ) {
790  $temp = $start;
791  $start = $end;
792  $end = $temp;
793  }
794 
795  // If year/month legacy filtering options are set, convert them to display the new stamp
796  if ( $year !== '' || $month !== '' ) {
797  // Reuse getDateCond logic, but subtract a day because
798  // the endpoints of our date range appear inclusive
799  // but the internal end offsets are always exclusive
800  $legacyTimestamp = ReverseChronologicalPager::getOffsetDate( $year, $month );
801  $legacyDateTime = new DateTime( $legacyTimestamp->getTimestamp( TS_ISO_8601 ) );
802  $legacyDateTime = $legacyDateTime->modify( '-1 day' );
803 
804  // Clear the new timestamp range options if used and
805  // replace with the converted legacy timestamp
806  $start = '';
807  $end = $legacyDateTime->format( 'Y-m-d' );
808  }
809 
810  $opts['start'] = $start;
811  $opts['end'] = $end;
812 
813  return $opts;
814  }
815 }
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:122
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:33
ContribsPager\getNamespace
getNamespace()
Definition: ContribsPager.php:461
HtmlArmor
Marks HTML that shouldn't be escaped.
Definition: HtmlArmor.php:28
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
Linker\userTalkLink
static userTalkLink( $userId, $userText)
Definition: Linker.php:1037
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:899
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:130
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:35
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:151
ContribsPager\getSqlComment
getSqlComment()
Overwrite Pager function and return a helpful comment.
Definition: ContribsPager.php:757
Linker\revComment
static revComment(Revision $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:1574
ContribsPager\formatRow
formatRow( $row)
Generates each row in the contributions list.
Definition: ContribsPager.php:572
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:537
ContribsPager\__construct
__construct(IContextSource $context, array $options, LinkRenderer $linkRenderer=null)
Definition: ContribsPager.php:102
IndexPager\$linkRenderer
LinkRenderer $linkRenderer
Definition: IndexPager.php:162
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:180
ContribsPager\$mDbSecondary
IDatabase $mDbSecondary
Definition: ContribsPager.php:90
ContribsPager\tryToCreateValidRevision
tryToCreateValidRevision( $row, $title=null)
Check whether the revision associated is valid for formatting.
Definition: ContribsPager.php:541
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:489
$revQuery
$revQuery
Definition: testCompression.php:53
ActorMigration\newMigration
static newMigration()
Static constructor.
Definition: ActorMigration.php:139
ContribsPager\processDateFilter
static processDateFilter(array $opts)
Set up date filter options, given request data.
Definition: ContribsPager.php:783
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:773
ChangeTags\modifyDisplayQuery
static modifyDisplayQuery(&$tables, &$fields, &$conds, &$join_conds, &$options, $filter_tag='')
Applies all tags-related changes to a query.
Definition: ChangeTags.php:776
ContribsPager\getNamespaceCond
getNamespaceCond()
Definition: ContribsPager.php:350
Revision\getQueryInfo
static getQueryInfo( $options=[])
Return the tables, fields, and join conditions to be selected to create a new revision object.
Definition: Revision.php:315
Linker\generateRollback
static generateRollback( $rev, IContextSource $context=null, $options=[ 'verify'])
Generate a rollback link for a given revision.
Definition: Linker.php:1808
ContribsPager\getTarget
getTarget()
Definition: ContribsPager.php:447
Wikimedia\Rdbms\IResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: IResultWrapper.php:24
ContribsPager\getStartBody
getStartBody()
Definition: ContribsPager.php:522
Title\newFromRow
static newFromRow( $row)
Make a Title object from a DB row.
Definition: Title.php:516
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2562
RangeChronologicalPager\getDateRangeCond
getDateRangeCond( $startStamp, $endStamp)
Set and return a date range condition using timestamps provided by the user.
Definition: RangeChronologicalPager.php:43
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:262
$title
$title
Definition: testCompression.php:36
ContribsPager\isNewOnly
isNewOnly()
Definition: ContribsPager.php:454
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:913
RangeChronologicalPager
Pager for filtering by a range of dates.
Definition: RangeChronologicalPager.php:27
ContextSource\msg
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:168
ContribsPager
Definition: ContribsPager.php:34
ContribsPager\isQueryableRange
isQueryableRange( $ipRange)
Is the given IP a range and within the CIDR limit?
Definition: ContribsPager.php:398
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:63
ContribsPager\getNavigationBar
getNavigationBar()
Wrap the navigation bar in a p element with identifying class.
Definition: ContribsPager.php:165
ContribsPager\$preventClickjacking
$preventClickjacking
Definition: ContribsPager.php:87
Revision\getParentLengths
static getParentLengths( $db, array $revIds)
Do a batched query to get the parent revision lengths.
Definition: Revision.php:342
Linker\getRevDeleteLink
static getRevDeleteLink(User $user, Revision $rev, LinkTarget $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2107
ContribsPager\getExtraSortFields
getExtraSortFields()
Definition: ContribsPager.php:468
ContribsPager\$templateParser
TemplateParser $templateParser
Definition: ContribsPager.php:100
ChangesList\revDateLink
static revDateLink(Revision $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:418
Linker\formatRevisionSize
static formatRevisionSize( $size)
Definition: Linker.php:1599
ChangesList\showCharacterDifference
static showCharacterDifference( $old, $new, IContextSource $context=null)
Show formatted char difference.
Definition: ChangesList.php:326
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:100
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:53
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:248
ContribsPager\getIpRangeConds
getIpRangeConds( $db, $ip)
Get SQL conditions for an IP range, if applicable.
Definition: ContribsPager.php:380
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:1065
ContribsPager\getEndBody
getEndBody()
Definition: ContribsPager.php:529
ContribsPager\getQueryInfo
getQueryInfo()
This function should be overridden to provide all parameters needed for the main paged query.
Definition: ContribsPager.php:264
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
ContribsPager\$namespace
string int $namespace
A single namespace number, or an empty string for all namespaces.
Definition: ContribsPager.php:49
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
ContribsPager\preventClickjacking
preventClickjacking()
Definition: ContribsPager.php:766
TemplateParser
Definition: TemplateParser.php:27
ContribsPager\getIndexField
getIndexField()
Definition: ContribsPager.php:416
ChangeTags\formatSummaryRow
static formatSummaryRow( $tags, $page, IContextSource $context=null)
Creates HTML for the given tags.
Definition: ChangeTags.php:99
ContribsPager\$mParentLens
array $mParentLens
Definition: ContribsPager.php:95
ContribsPager\getTagFilter
getTagFilter()
Definition: ContribsPager.php:440