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