MediaWiki  1.33.0
WatchedItemQueryService.php
Go to the documentation of this file.
1 <?php
2 
5 use Wikimedia\Assert\Assert;
7 
19 
20  const DIR_OLDER = 'older';
21  const DIR_NEWER = 'newer';
22 
23  const INCLUDE_FLAGS = 'flags';
24  const INCLUDE_USER = 'user';
25  const INCLUDE_USER_ID = 'userid';
26  const INCLUDE_COMMENT = 'comment';
27  const INCLUDE_PATROL_INFO = 'patrol';
28  const INCLUDE_AUTOPATROL_INFO = 'autopatrol';
29  const INCLUDE_SIZES = 'sizes';
30  const INCLUDE_LOG_INFO = 'loginfo';
31  const INCLUDE_TAGS = 'tags';
32 
33  // FILTER_* constants are part of public API (are used in ApiQueryWatchlist and
34  // ApiQueryWatchlistRaw classes) and should not be changed.
35  // Changing values of those constants will result in a breaking change in the API
36  const FILTER_MINOR = 'minor';
37  const FILTER_NOT_MINOR = '!minor';
38  const FILTER_BOT = 'bot';
39  const FILTER_NOT_BOT = '!bot';
40  const FILTER_ANON = 'anon';
41  const FILTER_NOT_ANON = '!anon';
42  const FILTER_PATROLLED = 'patrolled';
43  const FILTER_NOT_PATROLLED = '!patrolled';
44  const FILTER_AUTOPATROLLED = 'autopatrolled';
45  const FILTER_NOT_AUTOPATROLLED = '!autopatrolled';
46  const FILTER_UNREAD = 'unread';
47  const FILTER_NOT_UNREAD = '!unread';
48  const FILTER_CHANGED = 'changed';
49  const FILTER_NOT_CHANGED = '!changed';
50 
51  const SORT_ASC = 'ASC';
52  const SORT_DESC = 'DESC';
53 
57  private $loadBalancer;
58 
60  private $extensions = null;
61 
63  private $commentStore;
64 
66  private $actorMigration;
67 
70 
71  public function __construct(
76  ) {
77  $this->loadBalancer = $loadBalancer;
78  $this->commentStore = $commentStore;
79  $this->actorMigration = $actorMigration;
80  $this->watchedItemStore = $watchedItemStore;
81  }
82 
86  private function getExtensions() {
87  if ( $this->extensions === null ) {
88  $this->extensions = [];
89  Hooks::run( 'WatchedItemQueryServiceExtensions', [ &$this->extensions, $this ] );
90  }
91  return $this->extensions;
92  }
93 
97  private function getConnection() {
98  return $this->loadBalancer->getConnectionRef( DB_REPLICA, [ 'watchlist' ] );
99  }
100 
145  User $user, array $options = [], &$startFrom = null
146  ) {
147  $options += [
148  'includeFields' => [],
149  'namespaceIds' => [],
150  'filters' => [],
151  'allRevisions' => false,
152  'usedInGenerator' => false
153  ];
154 
155  Assert::parameter(
156  !isset( $options['rcTypes'] )
157  || !array_diff( $options['rcTypes'], [ RC_EDIT, RC_NEW, RC_LOG, RC_EXTERNAL, RC_CATEGORIZE ] ),
158  '$options[\'rcTypes\']',
159  'must be an array containing only: RC_EDIT, RC_NEW, RC_LOG, RC_EXTERNAL and/or RC_CATEGORIZE'
160  );
161  Assert::parameter(
162  !isset( $options['dir'] ) || in_array( $options['dir'], [ self::DIR_OLDER, self::DIR_NEWER ] ),
163  '$options[\'dir\']',
164  'must be DIR_OLDER or DIR_NEWER'
165  );
166  Assert::parameter(
167  !isset( $options['start'] ) && !isset( $options['end'] ) && $startFrom === null
168  || isset( $options['dir'] ),
169  '$options[\'dir\']',
170  'must be provided when providing the "start" or "end" options or the $startFrom parameter'
171  );
172  Assert::parameter(
173  !isset( $options['startFrom'] ),
174  '$options[\'startFrom\']',
175  'must not be provided, use $startFrom instead'
176  );
177  Assert::parameter(
178  !isset( $startFrom ) || ( is_array( $startFrom ) && count( $startFrom ) === 2 ),
179  '$startFrom',
180  'must be a two-element array'
181  );
182  if ( array_key_exists( 'watchlistOwner', $options ) ) {
183  Assert::parameterType(
184  User::class,
185  $options['watchlistOwner'],
186  '$options[\'watchlistOwner\']'
187  );
188  Assert::parameter(
189  isset( $options['watchlistOwnerToken'] ),
190  '$options[\'watchlistOwnerToken\']',
191  'must be provided when providing watchlistOwner option'
192  );
193  }
194 
195  $db = $this->getConnection();
196 
199  $conds = $this->getWatchedItemsWithRCInfoQueryConds( $db, $user, $options );
200  $dbOptions = $this->getWatchedItemsWithRCInfoQueryDbOptions( $options );
201  $joinConds = $this->getWatchedItemsWithRCInfoQueryJoinConds( $options );
202 
203  if ( $startFrom !== null ) {
204  $conds[] = $this->getStartFromConds( $db, $options, $startFrom );
205  }
206 
207  foreach ( $this->getExtensions() as $extension ) {
208  $extension->modifyWatchedItemsWithRCInfoQuery(
209  $user, $options, $db,
210  $tables,
211  $fields,
212  $conds,
213  $dbOptions,
214  $joinConds
215  );
216  }
217 
218  $res = $db->select(
219  $tables,
220  $fields,
221  $conds,
222  __METHOD__,
223  $dbOptions,
224  $joinConds
225  );
226 
227  $limit = $dbOptions['LIMIT'] ?? INF;
228  $items = [];
229  $startFrom = null;
230  foreach ( $res as $row ) {
231  if ( --$limit <= 0 ) {
232  $startFrom = [ $row->rc_timestamp, $row->rc_id ];
233  break;
234  }
235 
236  $target = new TitleValue( (int)$row->rc_namespace, $row->rc_title );
237  $items[] = [
238  new WatchedItem(
239  $user,
240  $target,
241  $this->watchedItemStore->getLatestNotificationTimestamp(
242  $row->wl_notificationtimestamp, $user, $target
243  )
244  ),
245  $this->getRecentChangeFieldsFromRow( $row )
246  ];
247  }
248 
249  foreach ( $this->getExtensions() as $extension ) {
250  $extension->modifyWatchedItemsWithRCInfo( $user, $options, $db, $items, $res, $startFrom );
251  }
252 
253  return $items;
254  }
255 
275  public function getWatchedItemsForUser( User $user, array $options = [] ) {
276  if ( $user->isAnon() ) {
277  // TODO: should this just return an empty array or rather complain loud at this point
278  // as e.g. ApiBase::getWatchlistUser does?
279  return [];
280  }
281 
282  $options += [ 'namespaceIds' => [] ];
283 
284  Assert::parameter(
285  !isset( $options['sort'] ) || in_array( $options['sort'], [ self::SORT_ASC, self::SORT_DESC ] ),
286  '$options[\'sort\']',
287  'must be SORT_ASC or SORT_DESC'
288  );
289  Assert::parameter(
290  !isset( $options['filter'] ) || in_array(
291  $options['filter'], [ self::FILTER_CHANGED, self::FILTER_NOT_CHANGED ]
292  ),
293  '$options[\'filter\']',
294  'must be FILTER_CHANGED or FILTER_NOT_CHANGED'
295  );
296  Assert::parameter(
297  !isset( $options['from'] ) && !isset( $options['until'] ) && !isset( $options['startFrom'] )
298  || isset( $options['sort'] ),
299  '$options[\'sort\']',
300  'must be provided if any of "from", "until", "startFrom" options is provided'
301  );
302 
303  $db = $this->getConnection();
304 
305  $conds = $this->getWatchedItemsForUserQueryConds( $db, $user, $options );
306  $dbOptions = $this->getWatchedItemsForUserQueryDbOptions( $options );
307 
308  $res = $db->select(
309  'watchlist',
310  [ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
311  $conds,
312  __METHOD__,
313  $dbOptions
314  );
315 
316  $watchedItems = [];
317  foreach ( $res as $row ) {
318  $target = new TitleValue( (int)$row->wl_namespace, $row->wl_title );
319  // todo these could all be cached at some point?
320  $watchedItems[] = new WatchedItem(
321  $user,
322  $target,
323  $this->watchedItemStore->getLatestNotificationTimestamp(
324  $row->wl_notificationtimestamp, $user, $target
325  )
326  );
327  }
328 
329  return $watchedItems;
330  }
331 
332  private function getRecentChangeFieldsFromRow( stdClass $row ) {
333  // FIXME: This can be simplified to single array_filter call filtering by key value,
334  // now we have stopped supporting PHP 5.5
335  $allFields = get_object_vars( $row );
336  $rcKeys = array_filter(
337  array_keys( $allFields ),
338  function ( $key ) {
339  return substr( $key, 0, 3 ) === 'rc_';
340  }
341  );
342  return array_intersect_key( $allFields, array_flip( $rcKeys ) );
343  }
344 
346  $tables = [ 'recentchanges', 'watchlist' ];
347  if ( !$options['allRevisions'] ) {
348  $tables[] = 'page';
349  }
350  if ( in_array( self::INCLUDE_COMMENT, $options['includeFields'] ) ) {
351  $tables += $this->commentStore->getJoin( 'rc_comment' )['tables'];
352  }
353  if ( in_array( self::INCLUDE_USER, $options['includeFields'] ) ||
354  in_array( self::INCLUDE_USER_ID, $options['includeFields'] ) ||
355  in_array( self::FILTER_ANON, $options['filters'] ) ||
356  in_array( self::FILTER_NOT_ANON, $options['filters'] ) ||
357  array_key_exists( 'onlyByUser', $options ) || array_key_exists( 'notByUser', $options )
358  ) {
359  $tables += $this->actorMigration->getJoin( 'rc_user' )['tables'];
360  }
361  return $tables;
362  }
363 
365  $fields = [
366  'rc_id',
367  'rc_namespace',
368  'rc_title',
369  'rc_timestamp',
370  'rc_type',
371  'rc_deleted',
372  'wl_notificationtimestamp'
373  ];
374 
375  $rcIdFields = [
376  'rc_cur_id',
377  'rc_this_oldid',
378  'rc_last_oldid',
379  ];
380  if ( $options['usedInGenerator'] ) {
381  if ( $options['allRevisions'] ) {
382  $rcIdFields = [ 'rc_this_oldid' ];
383  } else {
384  $rcIdFields = [ 'rc_cur_id' ];
385  }
386  }
387  $fields = array_merge( $fields, $rcIdFields );
388 
389  if ( in_array( self::INCLUDE_FLAGS, $options['includeFields'] ) ) {
390  $fields = array_merge( $fields, [ 'rc_type', 'rc_minor', 'rc_bot' ] );
391  }
392  if ( in_array( self::INCLUDE_USER, $options['includeFields'] ) ) {
393  $fields['rc_user_text'] = $this->actorMigration->getJoin( 'rc_user' )['fields']['rc_user_text'];
394  }
395  if ( in_array( self::INCLUDE_USER_ID, $options['includeFields'] ) ) {
396  $fields['rc_user'] = $this->actorMigration->getJoin( 'rc_user' )['fields']['rc_user'];
397  }
398  if ( in_array( self::INCLUDE_COMMENT, $options['includeFields'] ) ) {
399  $fields += $this->commentStore->getJoin( 'rc_comment' )['fields'];
400  }
401  if ( in_array( self::INCLUDE_PATROL_INFO, $options['includeFields'] ) ) {
402  $fields = array_merge( $fields, [ 'rc_patrolled', 'rc_log_type' ] );
403  }
404  if ( in_array( self::INCLUDE_SIZES, $options['includeFields'] ) ) {
405  $fields = array_merge( $fields, [ 'rc_old_len', 'rc_new_len' ] );
406  }
407  if ( in_array( self::INCLUDE_LOG_INFO, $options['includeFields'] ) ) {
408  $fields = array_merge( $fields, [ 'rc_logid', 'rc_log_type', 'rc_log_action', 'rc_params' ] );
409  }
410  if ( in_array( self::INCLUDE_TAGS, $options['includeFields'] ) ) {
411  // prefixed with rc_ to include the field in getRecentChangeFieldsFromRow
412  $fields['rc_tags'] = ChangeTags::makeTagSummarySubquery( 'recentchanges' );
413  }
414 
415  return $fields;
416  }
417 
419  IDatabase $db,
420  User $user,
422  ) {
423  $watchlistOwnerId = $this->getWatchlistOwnerId( $user, $options );
424  $conds = [ 'wl_user' => $watchlistOwnerId ];
425 
426  if ( !$options['allRevisions'] ) {
427  $conds[] = $db->makeList(
428  [ 'rc_this_oldid=page_latest', 'rc_type=' . RC_LOG ],
429  LIST_OR
430  );
431  }
432 
433  if ( $options['namespaceIds'] ) {
434  $conds['wl_namespace'] = array_map( 'intval', $options['namespaceIds'] );
435  }
436 
437  if ( array_key_exists( 'rcTypes', $options ) ) {
438  $conds['rc_type'] = array_map( 'intval', $options['rcTypes'] );
439  }
440 
441  $conds = array_merge(
442  $conds,
444  );
445 
446  $conds = array_merge( $conds, $this->getStartEndConds( $db, $options ) );
447 
448  if ( !isset( $options['start'] ) && !isset( $options['end'] ) && $db->getType() === 'mysql' ) {
449  // This is an index optimization for mysql
450  $conds[] = 'rc_timestamp > ' . $db->addQuotes( '' );
451  }
452 
453  $conds = array_merge( $conds, $this->getUserRelatedConds( $db, $user, $options ) );
454 
455  $deletedPageLogCond = $this->getExtraDeletedPageLogEntryRelatedCond( $db, $user );
456  if ( $deletedPageLogCond ) {
457  $conds[] = $deletedPageLogCond;
458  }
459 
460  return $conds;
461  }
462 
463  private function getWatchlistOwnerId( User $user, array $options ) {
464  if ( array_key_exists( 'watchlistOwner', $options ) ) {
466  $watchlistOwner = $options['watchlistOwner'];
467  $ownersToken = $watchlistOwner->getOption( 'watchlisttoken' );
468  $token = $options['watchlistOwnerToken'];
469  if ( $ownersToken == '' || !hash_equals( $ownersToken, $token ) ) {
470  throw ApiUsageException::newWithMessage( null, 'apierror-bad-watchlist-token', 'bad_wltoken' );
471  }
472  return $watchlistOwner->getId();
473  }
474  return $user->getId();
475  }
476 
478  $conds = [];
479 
480  if ( in_array( self::FILTER_MINOR, $options['filters'] ) ) {
481  $conds[] = 'rc_minor != 0';
482  } elseif ( in_array( self::FILTER_NOT_MINOR, $options['filters'] ) ) {
483  $conds[] = 'rc_minor = 0';
484  }
485 
486  if ( in_array( self::FILTER_BOT, $options['filters'] ) ) {
487  $conds[] = 'rc_bot != 0';
488  } elseif ( in_array( self::FILTER_NOT_BOT, $options['filters'] ) ) {
489  $conds[] = 'rc_bot = 0';
490  }
491 
492  if ( in_array( self::FILTER_ANON, $options['filters'] ) ) {
493  $conds[] = $this->actorMigration->isAnon(
494  $this->actorMigration->getJoin( 'rc_user' )['fields']['rc_user']
495  );
496  } elseif ( in_array( self::FILTER_NOT_ANON, $options['filters'] ) ) {
497  $conds[] = $this->actorMigration->isNotAnon(
498  $this->actorMigration->getJoin( 'rc_user' )['fields']['rc_user']
499  );
500  }
501 
502  if ( $user->useRCPatrol() || $user->useNPPatrol() ) {
503  // TODO: not sure if this should simply ignore patrolled filters if user does not have the patrol
504  // right, or maybe rather fail loud at this point, same as e.g. ApiQueryWatchlist does?
505  if ( in_array( self::FILTER_PATROLLED, $options['filters'] ) ) {
506  $conds[] = 'rc_patrolled != ' . RecentChange::PRC_UNPATROLLED;
507  } elseif ( in_array( self::FILTER_NOT_PATROLLED, $options['filters'] ) ) {
508  $conds['rc_patrolled'] = RecentChange::PRC_UNPATROLLED;
509  }
510 
511  if ( in_array( self::FILTER_AUTOPATROLLED, $options['filters'] ) ) {
512  $conds['rc_patrolled'] = RecentChange::PRC_AUTOPATROLLED;
513  } elseif ( in_array( self::FILTER_NOT_AUTOPATROLLED, $options['filters'] ) ) {
514  $conds[] = 'rc_patrolled != ' . RecentChange::PRC_AUTOPATROLLED;
515  }
516  }
517 
518  if ( in_array( self::FILTER_UNREAD, $options['filters'] ) ) {
519  $conds[] = 'rc_timestamp >= wl_notificationtimestamp';
520  } elseif ( in_array( self::FILTER_NOT_UNREAD, $options['filters'] ) ) {
521  // TODO: should this be changed to use Database::makeList?
522  $conds[] = 'wl_notificationtimestamp IS NULL OR rc_timestamp < wl_notificationtimestamp';
523  }
524 
525  return $conds;
526  }
527 
528  private function getStartEndConds( IDatabase $db, array $options ) {
529  if ( !isset( $options['start'] ) && !isset( $options['end'] ) ) {
530  return [];
531  }
532 
533  $conds = [];
534 
535  if ( isset( $options['start'] ) ) {
536  $after = $options['dir'] === self::DIR_OLDER ? '<=' : '>=';
537  $conds[] = 'rc_timestamp ' . $after . ' ' .
538  $db->addQuotes( $db->timestamp( $options['start'] ) );
539  }
540  if ( isset( $options['end'] ) ) {
541  $before = $options['dir'] === self::DIR_OLDER ? '>=' : '<=';
542  $conds[] = 'rc_timestamp ' . $before . ' ' .
543  $db->addQuotes( $db->timestamp( $options['end'] ) );
544  }
545 
546  return $conds;
547  }
548 
549  private function getUserRelatedConds( IDatabase $db, User $user, array $options ) {
550  if ( !array_key_exists( 'onlyByUser', $options ) && !array_key_exists( 'notByUser', $options ) ) {
551  return [];
552  }
553 
554  $conds = [];
555 
556  if ( array_key_exists( 'onlyByUser', $options ) ) {
557  $byUser = User::newFromName( $options['onlyByUser'], false );
558  $conds[] = $this->actorMigration->getWhere( $db, 'rc_user', $byUser )['conds'];
559  } elseif ( array_key_exists( 'notByUser', $options ) ) {
560  $byUser = User::newFromName( $options['notByUser'], false );
561  $conds[] = 'NOT(' . $this->actorMigration->getWhere( $db, 'rc_user', $byUser )['conds'] . ')';
562  }
563 
564  // Avoid brute force searches (T19342)
565  $bitmask = 0;
566  if ( !$user->isAllowed( 'deletedhistory' ) ) {
567  $bitmask = Revision::DELETED_USER;
568  } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
570  }
571  if ( $bitmask ) {
572  $conds[] = $db->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask";
573  }
574 
575  return $conds;
576  }
577 
579  // LogPage::DELETED_ACTION hides the affected page, too. So hide those
580  // entirely from the watchlist, or someone could guess the title.
581  $bitmask = 0;
582  if ( !$user->isAllowed( 'deletedhistory' ) ) {
583  $bitmask = LogPage::DELETED_ACTION;
584  } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
586  }
587  if ( $bitmask ) {
588  return $db->makeList( [
589  'rc_type != ' . RC_LOG,
590  $db->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask",
591  ], LIST_OR );
592  }
593  return '';
594  }
595 
596  private function getStartFromConds( IDatabase $db, array $options, array $startFrom ) {
597  $op = $options['dir'] === self::DIR_OLDER ? '<' : '>';
598  list( $rcTimestamp, $rcId ) = $startFrom;
599  $rcTimestamp = $db->addQuotes( $db->timestamp( $rcTimestamp ) );
600  $rcId = (int)$rcId;
601  return $db->makeList(
602  [
603  "rc_timestamp $op $rcTimestamp",
604  $db->makeList(
605  [
606  "rc_timestamp = $rcTimestamp",
607  "rc_id $op= $rcId"
608  ],
609  LIST_AND
610  )
611  ],
612  LIST_OR
613  );
614  }
615 
617  $conds = [ 'wl_user' => $user->getId() ];
618  if ( $options['namespaceIds'] ) {
619  $conds['wl_namespace'] = array_map( 'intval', $options['namespaceIds'] );
620  }
621  if ( isset( $options['filter'] ) ) {
622  $filter = $options['filter'];
623  if ( $filter === self::FILTER_CHANGED ) {
624  $conds[] = 'wl_notificationtimestamp IS NOT NULL';
625  } else {
626  $conds[] = 'wl_notificationtimestamp IS NULL';
627  }
628  }
629 
630  if ( isset( $options['from'] ) ) {
631  $op = $options['sort'] === self::SORT_ASC ? '>' : '<';
632  $conds[] = $this->getFromUntilTargetConds( $db, $options['from'], $op );
633  }
634  if ( isset( $options['until'] ) ) {
635  $op = $options['sort'] === self::SORT_ASC ? '<' : '>';
636  $conds[] = $this->getFromUntilTargetConds( $db, $options['until'], $op );
637  }
638  if ( isset( $options['startFrom'] ) ) {
639  $op = $options['sort'] === self::SORT_ASC ? '>' : '<';
640  $conds[] = $this->getFromUntilTargetConds( $db, $options['startFrom'], $op );
641  }
642 
643  return $conds;
644  }
645 
655  private function getFromUntilTargetConds( IDatabase $db, LinkTarget $target, $op ) {
656  return $db->makeList(
657  [
658  "wl_namespace $op " . $target->getNamespace(),
659  $db->makeList(
660  [
661  'wl_namespace = ' . $target->getNamespace(),
662  "wl_title $op= " . $db->addQuotes( $target->getDBkey() )
663  ],
664  LIST_AND
665  )
666  ],
667  LIST_OR
668  );
669  }
670 
672  $dbOptions = [];
673 
674  if ( array_key_exists( 'dir', $options ) ) {
675  $sort = $options['dir'] === self::DIR_OLDER ? ' DESC' : '';
676  $dbOptions['ORDER BY'] = [ 'rc_timestamp' . $sort, 'rc_id' . $sort ];
677  }
678 
679  if ( array_key_exists( 'limit', $options ) ) {
680  $dbOptions['LIMIT'] = (int)$options['limit'] + 1;
681  }
682 
683  return $dbOptions;
684  }
685 
687  $dbOptions = [];
688  if ( array_key_exists( 'sort', $options ) ) {
689  $dbOptions['ORDER BY'] = [
690  "wl_namespace {$options['sort']}",
691  "wl_title {$options['sort']}"
692  ];
693  if ( count( $options['namespaceIds'] ) === 1 ) {
694  $dbOptions['ORDER BY'] = "wl_title {$options['sort']}";
695  }
696  }
697  if ( array_key_exists( 'limit', $options ) ) {
698  $dbOptions['LIMIT'] = (int)$options['limit'];
699  }
700  return $dbOptions;
701  }
702 
704  $joinConds = [
705  'watchlist' => [ 'JOIN',
706  [
707  'wl_namespace=rc_namespace',
708  'wl_title=rc_title'
709  ]
710  ]
711  ];
712  if ( !$options['allRevisions'] ) {
713  $joinConds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
714  }
715  if ( in_array( self::INCLUDE_COMMENT, $options['includeFields'] ) ) {
716  $joinConds += $this->commentStore->getJoin( 'rc_comment' )['joins'];
717  }
718  if ( in_array( self::INCLUDE_USER, $options['includeFields'] ) ||
719  in_array( self::INCLUDE_USER_ID, $options['includeFields'] ) ||
720  in_array( self::FILTER_ANON, $options['filters'] ) ||
721  in_array( self::FILTER_NOT_ANON, $options['filters'] ) ||
722  array_key_exists( 'onlyByUser', $options ) || array_key_exists( 'notByUser', $options )
723  ) {
724  $joinConds += $this->actorMigration->getJoin( 'rc_user' )['joins'];
725  }
726  return $joinConds;
727  }
728 
729 }
$filter
$filter
Definition: profileinfo.php:341
WatchedItemQueryService\getConnection
getConnection()
Definition: WatchedItemQueryService.php:97
Revision\DELETED_USER
const DELETED_USER
Definition: Revision.php:48
ChangeTags\makeTagSummarySubquery
static makeTagSummarySubquery( $tables)
Make the tag summary subquery based on the given tables and return it.
Definition: ChangeTags.php:793
$user
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1476
Revision\DELETED_RESTRICTED
const DELETED_RESTRICTED
Definition: Revision.php:49
WatchedItemQueryService\getFromUntilTargetConds
getFromUntilTargetConds(IDatabase $db, LinkTarget $target, $op)
Creates a query condition part for getting only items before or after the given link target (while or...
Definition: WatchedItemQueryService.php:655
RC_EXTERNAL
const RC_EXTERNAL
Definition: Defines.php:145
WatchedItemQueryService\getWatchedItemsWithRCInfoQueryConds
getWatchedItemsWithRCInfoQueryConds(IDatabase $db, User $user, array $options)
Definition: WatchedItemQueryService.php:418
WatchedItemQueryService\getWatchedItemsWithRCInfoQueryJoinConds
getWatchedItemsWithRCInfoQueryJoinConds(array $options)
Definition: WatchedItemQueryService.php:703
WatchedItemQueryServiceExtension
Definition: WatchedItemQueryServiceExtension.php:16
WatchedItemQueryService\getUserRelatedConds
getUserRelatedConds(IDatabase $db, User $user, array $options)
Definition: WatchedItemQueryService.php:549
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
WatchedItemQueryService\$commentStore
CommentStore $commentStore
Definition: WatchedItemQueryService.php:63
WatchedItemQueryService\INCLUDE_COMMENT
const INCLUDE_COMMENT
Definition: WatchedItemQueryService.php:26
WatchedItemQueryService\$loadBalancer
LoadBalancer $loadBalancer
Definition: WatchedItemQueryService.php:57
WatchedItemQueryService\getWatchedItemsForUserQueryConds
getWatchedItemsForUserQueryConds(IDatabase $db, User $user, array $options)
Definition: WatchedItemQueryService.php:616
Wikimedia\Rdbms\IDatabase\makeList
makeList( $a, $mode=self::LIST_COMMA)
Makes an encoded list of strings from an array.
WatchedItemQueryService\FILTER_NOT_AUTOPATROLLED
const FILTER_NOT_AUTOPATROLLED
Definition: WatchedItemQueryService.php:45
WatchedItemQueryService\FILTER_PATROLLED
const FILTER_PATROLLED
Definition: WatchedItemQueryService.php:42
captcha-old.count
count
Definition: captcha-old.py:249
WatchedItemQueryService\getWatchedItemsWithRCInfoQueryFields
getWatchedItemsWithRCInfoQueryFields(array $options)
Definition: WatchedItemQueryService.php:364
WatchedItemQueryService\getStartEndConds
getStartEndConds(IDatabase $db, array $options)
Definition: WatchedItemQueryService.php:528
RC_LOG
const RC_LOG
Definition: Defines.php:144
$tables
this hook is for auditing only RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist & $tables
Definition: hooks.txt:979
WatchedItemQueryService\getExtraDeletedPageLogEntryRelatedCond
getExtraDeletedPageLogEntryRelatedCond(IDatabase $db, User $user)
Definition: WatchedItemQueryService.php:578
WatchedItemQueryService\getWatchedItemsWithRCInfoQueryDbOptions
getWatchedItemsWithRCInfoQueryDbOptions(array $options)
Definition: WatchedItemQueryService.php:671
CommentStore
CommentStore handles storage of comments (edit summaries, log reasons, etc) in the database.
Definition: CommentStore.php:31
RC_EDIT
const RC_EDIT
Definition: Defines.php:142
WatchedItemQueryService\getWatchedItemsForUserQueryDbOptions
getWatchedItemsForUserQueryDbOptions(array $options)
Definition: WatchedItemQueryService.php:686
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:585
$res
$res
Definition: database.txt:21
WatchedItemQueryService\getWatchedItemsWithRecentChangeInfo
getWatchedItemsWithRecentChangeInfo(User $user, array $options=[], &$startFrom=null)
Definition: WatchedItemQueryService.php:144
ActorMigration
This class handles the logic for the actor table migration.
Definition: ActorMigration.php:35
WatchedItemQueryService\$actorMigration
ActorMigration $actorMigration
Definition: WatchedItemQueryService.php:66
ApiUsageException\newWithMessage
static newWithMessage(ApiBase $module=null, $msg, $code=null, $data=null, $httpCode=0)
Definition: ApiUsageException.php:63
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
LIST_AND
const LIST_AND
Definition: Defines.php:43
WatchedItemQueryService\INCLUDE_TAGS
const INCLUDE_TAGS
Definition: WatchedItemQueryService.php:31
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
WatchedItemQueryService\INCLUDE_LOG_INFO
const INCLUDE_LOG_INFO
Definition: WatchedItemQueryService.php:30
MediaWiki\Linker\LinkTarget\getNamespace
getNamespace()
Get the namespace index.
Wikimedia\Rdbms\IDatabase\timestamp
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by wfTimestamp() to the format used for inserting ...
LIST_OR
const LIST_OR
Definition: Defines.php:46
WatchedItemQueryService\$watchedItemStore
WatchedItemStoreInterface $watchedItemStore
Definition: WatchedItemQueryService.php:69
WatchedItemQueryService\getWatchedItemsWithRCInfoQueryFilterConds
getWatchedItemsWithRCInfoQueryFilterConds(User $user, array $options)
Definition: WatchedItemQueryService.php:477
WatchedItemQueryService\FILTER_MINOR
const FILTER_MINOR
Definition: WatchedItemQueryService.php:36
WatchedItemQueryService\FILTER_NOT_CHANGED
const FILTER_NOT_CHANGED
Definition: WatchedItemQueryService.php:49
WatchedItemQueryService\INCLUDE_AUTOPATROL_INFO
const INCLUDE_AUTOPATROL_INFO
Definition: WatchedItemQueryService.php:28
WatchedItemQueryService\INCLUDE_USER
const INCLUDE_USER
Definition: WatchedItemQueryService.php:24
WatchedItemQueryService\FILTER_NOT_BOT
const FILTER_NOT_BOT
Definition: WatchedItemQueryService.php:39
WatchedItemQueryService
Definition: WatchedItemQueryService.php:18
WatchedItemQueryService\INCLUDE_PATROL_INFO
const INCLUDE_PATROL_INFO
Definition: WatchedItemQueryService.php:27
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
WatchedItemQueryService\FILTER_BOT
const FILTER_BOT
Definition: WatchedItemQueryService.php:38
WatchedItemQueryService\INCLUDE_USER_ID
const INCLUDE_USER_ID
Definition: WatchedItemQueryService.php:25
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
array
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
$sort
$sort
Definition: profileinfo.php:328
LogPage\DELETED_ACTION
const DELETED_ACTION
Definition: LogPage.php:34
Wikimedia\Rdbms\LoadBalancer
Database connection, tracking, load balancing, and transaction manager for a cluster.
Definition: LoadBalancer.php:41
WatchedItemQueryService\DIR_NEWER
const DIR_NEWER
Definition: WatchedItemQueryService.php:21
WatchedItemQueryService\SORT_DESC
const SORT_DESC
Definition: WatchedItemQueryService.php:52
extensions
The ContentHandler facility adds support for arbitrary content types on wiki instead of relying on wikitext for everything It was introduced in MediaWiki Each kind of and so on Built in content types as usual *javascript user provided javascript code *json simple implementation for use by extensions
Definition: contenthandler.txt:5
WatchedItemQueryService\getRecentChangeFieldsFromRow
getRecentChangeFieldsFromRow(stdClass $row)
Definition: WatchedItemQueryService.php:332
WatchedItem
Representation of a pair of user and title for watchlist entries.
Definition: WatchedItem.php:32
WatchedItemQueryService\FILTER_NOT_PATROLLED
const FILTER_NOT_PATROLLED
Definition: WatchedItemQueryService.php:43
MediaWiki\Linker\LinkTarget\getDBkey
getDBkey()
Get the main part with underscores.
WatchedItemQueryService\SORT_ASC
const SORT_ASC
Definition: WatchedItemQueryService.php:51
WatchedItemQueryService\FILTER_NOT_MINOR
const FILTER_NOT_MINOR
Definition: WatchedItemQueryService.php:37
RC_NEW
const RC_NEW
Definition: Defines.php:143
WatchedItemQueryService\INCLUDE_FLAGS
const INCLUDE_FLAGS
Definition: WatchedItemQueryService.php:23
RecentChange\PRC_AUTOPATROLLED
const PRC_AUTOPATROLLED
Definition: RecentChange.php:80
Wikimedia\Rdbms\IDatabase\bitAnd
bitAnd( $fieldLeft, $fieldRight)
WatchedItemQueryService\DIR_OLDER
const DIR_OLDER
Definition: WatchedItemQueryService.php:20
WatchedItemQueryService\getExtensions
getExtensions()
Definition: WatchedItemQueryService.php:86
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1985
Wikimedia\Rdbms\IDatabase\addQuotes
addQuotes( $s)
Adds quotes and backslashes.
WatchedItemQueryService\FILTER_CHANGED
const FILTER_CHANGED
Definition: WatchedItemQueryService.php:48
WatchedItemQueryService\FILTER_AUTOPATROLLED
const FILTER_AUTOPATROLLED
Definition: WatchedItemQueryService.php:44
WatchedItemQueryService\FILTER_NOT_UNREAD
const FILTER_NOT_UNREAD
Definition: WatchedItemQueryService.php:47
RecentChange\PRC_UNPATROLLED
const PRC_UNPATROLLED
Definition: RecentChange.php:78
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
WatchedItemQueryService\__construct
__construct(LoadBalancer $loadBalancer, CommentStore $commentStore, ActorMigration $actorMigration, WatchedItemStoreInterface $watchedItemStore)
Definition: WatchedItemQueryService.php:71
WatchedItemQueryService\getWatchlistOwnerId
getWatchlistOwnerId(User $user, array $options)
Definition: WatchedItemQueryService.php:463
WatchedItemQueryService\FILTER_NOT_ANON
const FILTER_NOT_ANON
Definition: WatchedItemQueryService.php:41
Wikimedia\Rdbms\IDatabase\getType
getType()
Get the type of the DBMS, as it appears in $wgDBtype.
WatchedItemQueryService\getWatchedItemsWithRCInfoQueryTables
getWatchedItemsWithRCInfoQueryTables(array $options)
Definition: WatchedItemQueryService.php:345
LogPage\DELETED_RESTRICTED
const DELETED_RESTRICTED
Definition: LogPage.php:37
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
RC_CATEGORIZE
const RC_CATEGORIZE
Definition: Defines.php:146
WatchedItemQueryService\INCLUDE_SIZES
const INCLUDE_SIZES
Definition: WatchedItemQueryService.php:29
MediaWiki\Linker\LinkTarget
Definition: LinkTarget.php:26
WatchedItemQueryService\getWatchedItemsForUser
getWatchedItemsForUser(User $user, array $options=[])
For simple listing of user's watchlist items, see WatchedItemStore::getWatchedItemsForUser.
Definition: WatchedItemQueryService.php:275
WatchedItemStoreInterface
Definition: WatchedItemStoreInterface.php:28
WatchedItemQueryService\FILTER_ANON
const FILTER_ANON
Definition: WatchedItemQueryService.php:40
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:48
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
WatchedItemQueryService\FILTER_UNREAD
const FILTER_UNREAD
Definition: WatchedItemQueryService.php:46
WatchedItemQueryService\$extensions
WatchedItemQueryServiceExtension[] null $extensions
Definition: WatchedItemQueryService.php:60
TitleValue
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:36
WatchedItemQueryService\getStartFromConds
getStartFromConds(IDatabase $db, array $options, array $startFrom)
Definition: WatchedItemQueryService.php:596