MediaWiki  1.30.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_SIZES = 'sizes';
29  const INCLUDE_LOG_INFO = 'loginfo';
30 
31  // FILTER_* constants are part of public API (are used in ApiQueryWatchlist and
32  // ApiQueryWatchlistRaw classes) and should not be changed.
33  // Changing values of those constants will result in a breaking change in the API
34  const FILTER_MINOR = 'minor';
35  const FILTER_NOT_MINOR = '!minor';
36  const FILTER_BOT = 'bot';
37  const FILTER_NOT_BOT = '!bot';
38  const FILTER_ANON = 'anon';
39  const FILTER_NOT_ANON = '!anon';
40  const FILTER_PATROLLED = 'patrolled';
41  const FILTER_NOT_PATROLLED = '!patrolled';
42  const FILTER_UNREAD = 'unread';
43  const FILTER_NOT_UNREAD = '!unread';
44  const FILTER_CHANGED = 'changed';
45  const FILTER_NOT_CHANGED = '!changed';
46 
47  const SORT_ASC = 'ASC';
48  const SORT_DESC = 'DESC';
49 
53  private $loadBalancer;
54 
56  private $extensions = null;
57 
60  private $commentStore = null;
61 
62  public function __construct( LoadBalancer $loadBalancer ) {
63  $this->loadBalancer = $loadBalancer;
64  }
65 
69  private function getExtensions() {
70  if ( $this->extensions === null ) {
71  $this->extensions = [];
72  Hooks::run( 'WatchedItemQueryServiceExtensions', [ &$this->extensions, $this ] );
73  }
74  return $this->extensions;
75  }
76 
81  private function getConnection() {
82  return $this->loadBalancer->getConnectionRef( DB_REPLICA, [ 'watchlist' ] );
83  }
84 
85  private function getCommentStore() {
86  if ( !$this->commentStore ) {
87  $this->commentStore = new CommentStore( 'rc_comment' );
88  }
89  return $this->commentStore;
90  }
91 
136  User $user, array $options = [], &$startFrom = null
137  ) {
138  $options += [
139  'includeFields' => [],
140  'namespaceIds' => [],
141  'filters' => [],
142  'allRevisions' => false,
143  'usedInGenerator' => false
144  ];
145 
146  Assert::parameter(
147  !isset( $options['rcTypes'] )
148  || !array_diff( $options['rcTypes'], [ RC_EDIT, RC_NEW, RC_LOG, RC_EXTERNAL, RC_CATEGORIZE ] ),
149  '$options[\'rcTypes\']',
150  'must be an array containing only: RC_EDIT, RC_NEW, RC_LOG, RC_EXTERNAL and/or RC_CATEGORIZE'
151  );
152  Assert::parameter(
153  !isset( $options['dir'] ) || in_array( $options['dir'], [ self::DIR_OLDER, self::DIR_NEWER ] ),
154  '$options[\'dir\']',
155  'must be DIR_OLDER or DIR_NEWER'
156  );
157  Assert::parameter(
158  !isset( $options['start'] ) && !isset( $options['end'] ) && $startFrom === null
159  || isset( $options['dir'] ),
160  '$options[\'dir\']',
161  'must be provided when providing the "start" or "end" options or the $startFrom parameter'
162  );
163  Assert::parameter(
164  !isset( $options['startFrom'] ),
165  '$options[\'startFrom\']',
166  'must not be provided, use $startFrom instead'
167  );
168  Assert::parameter(
169  !isset( $startFrom ) || ( is_array( $startFrom ) && count( $startFrom ) === 2 ),
170  '$startFrom',
171  'must be a two-element array'
172  );
173  if ( array_key_exists( 'watchlistOwner', $options ) ) {
174  Assert::parameterType(
175  User::class,
176  $options['watchlistOwner'],
177  '$options[\'watchlistOwner\']'
178  );
179  Assert::parameter(
180  isset( $options['watchlistOwnerToken'] ),
181  '$options[\'watchlistOwnerToken\']',
182  'must be provided when providing watchlistOwner option'
183  );
184  }
185 
186  $db = $this->getConnection();
187 
190  $conds = $this->getWatchedItemsWithRCInfoQueryConds( $db, $user, $options );
191  $dbOptions = $this->getWatchedItemsWithRCInfoQueryDbOptions( $options );
192  $joinConds = $this->getWatchedItemsWithRCInfoQueryJoinConds( $options );
193 
194  if ( $startFrom !== null ) {
195  $conds[] = $this->getStartFromConds( $db, $options, $startFrom );
196  }
197 
198  foreach ( $this->getExtensions() as $extension ) {
199  $extension->modifyWatchedItemsWithRCInfoQuery(
200  $user, $options, $db,
201  $tables,
202  $fields,
203  $conds,
204  $dbOptions,
205  $joinConds
206  );
207  }
208 
209  $res = $db->select(
210  $tables,
211  $fields,
212  $conds,
213  __METHOD__,
214  $dbOptions,
215  $joinConds
216  );
217 
218  $limit = isset( $dbOptions['LIMIT'] ) ? $dbOptions['LIMIT'] : INF;
219  $items = [];
220  $startFrom = null;
221  foreach ( $res as $row ) {
222  if ( --$limit <= 0 ) {
223  $startFrom = [ $row->rc_timestamp, $row->rc_id ];
224  break;
225  }
226 
227  $items[] = [
228  new WatchedItem(
229  $user,
230  new TitleValue( (int)$row->rc_namespace, $row->rc_title ),
231  $row->wl_notificationtimestamp
232  ),
233  $this->getRecentChangeFieldsFromRow( $row )
234  ];
235  }
236 
237  foreach ( $this->getExtensions() as $extension ) {
238  $extension->modifyWatchedItemsWithRCInfo( $user, $options, $db, $items, $res, $startFrom );
239  }
240 
241  return $items;
242  }
243 
263  public function getWatchedItemsForUser( User $user, array $options = [] ) {
264  if ( $user->isAnon() ) {
265  // TODO: should this just return an empty array or rather complain loud at this point
266  // as e.g. ApiBase::getWatchlistUser does?
267  return [];
268  }
269 
270  $options += [ 'namespaceIds' => [] ];
271 
272  Assert::parameter(
273  !isset( $options['sort'] ) || in_array( $options['sort'], [ self::SORT_ASC, self::SORT_DESC ] ),
274  '$options[\'sort\']',
275  'must be SORT_ASC or SORT_DESC'
276  );
277  Assert::parameter(
278  !isset( $options['filter'] ) || in_array(
279  $options['filter'], [ self::FILTER_CHANGED, self::FILTER_NOT_CHANGED ]
280  ),
281  '$options[\'filter\']',
282  'must be FILTER_CHANGED or FILTER_NOT_CHANGED'
283  );
284  Assert::parameter(
285  !isset( $options['from'] ) && !isset( $options['until'] ) && !isset( $options['startFrom'] )
286  || isset( $options['sort'] ),
287  '$options[\'sort\']',
288  'must be provided if any of "from", "until", "startFrom" options is provided'
289  );
290 
291  $db = $this->getConnection();
292 
293  $conds = $this->getWatchedItemsForUserQueryConds( $db, $user, $options );
294  $dbOptions = $this->getWatchedItemsForUserQueryDbOptions( $options );
295 
296  $res = $db->select(
297  'watchlist',
298  [ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
299  $conds,
300  __METHOD__,
301  $dbOptions
302  );
303 
304  $watchedItems = [];
305  foreach ( $res as $row ) {
306  // todo these could all be cached at some point?
307  $watchedItems[] = new WatchedItem(
308  $user,
309  new TitleValue( (int)$row->wl_namespace, $row->wl_title ),
310  $row->wl_notificationtimestamp
311  );
312  }
313 
314  return $watchedItems;
315  }
316 
317  private function getRecentChangeFieldsFromRow( stdClass $row ) {
318  // This can be simplified to single array_filter call filtering by key value,
319  // once we stop supporting PHP 5.5
320  $allFields = get_object_vars( $row );
321  $rcKeys = array_filter(
322  array_keys( $allFields ),
323  function ( $key ) {
324  return substr( $key, 0, 3 ) === 'rc_';
325  }
326  );
327  return array_intersect_key( $allFields, array_flip( $rcKeys ) );
328  }
329 
331  $tables = [ 'recentchanges', 'watchlist' ];
332  if ( !$options['allRevisions'] ) {
333  $tables[] = 'page';
334  }
335  if ( in_array( self::INCLUDE_COMMENT, $options['includeFields'] ) ) {
336  $tables += $this->getCommentStore()->getJoin()['tables'];
337  }
338  return $tables;
339  }
340 
342  $fields = [
343  'rc_id',
344  'rc_namespace',
345  'rc_title',
346  'rc_timestamp',
347  'rc_type',
348  'rc_deleted',
349  'wl_notificationtimestamp'
350  ];
351 
352  $rcIdFields = [
353  'rc_cur_id',
354  'rc_this_oldid',
355  'rc_last_oldid',
356  ];
357  if ( $options['usedInGenerator'] ) {
358  if ( $options['allRevisions'] ) {
359  $rcIdFields = [ 'rc_this_oldid' ];
360  } else {
361  $rcIdFields = [ 'rc_cur_id' ];
362  }
363  }
364  $fields = array_merge( $fields, $rcIdFields );
365 
366  if ( in_array( self::INCLUDE_FLAGS, $options['includeFields'] ) ) {
367  $fields = array_merge( $fields, [ 'rc_type', 'rc_minor', 'rc_bot' ] );
368  }
369  if ( in_array( self::INCLUDE_USER, $options['includeFields'] ) ) {
370  $fields[] = 'rc_user_text';
371  }
372  if ( in_array( self::INCLUDE_USER_ID, $options['includeFields'] ) ) {
373  $fields[] = 'rc_user';
374  }
375  if ( in_array( self::INCLUDE_COMMENT, $options['includeFields'] ) ) {
376  $fields += $this->getCommentStore()->getJoin()['fields'];
377  }
378  if ( in_array( self::INCLUDE_PATROL_INFO, $options['includeFields'] ) ) {
379  $fields = array_merge( $fields, [ 'rc_patrolled', 'rc_log_type' ] );
380  }
381  if ( in_array( self::INCLUDE_SIZES, $options['includeFields'] ) ) {
382  $fields = array_merge( $fields, [ 'rc_old_len', 'rc_new_len' ] );
383  }
384  if ( in_array( self::INCLUDE_LOG_INFO, $options['includeFields'] ) ) {
385  $fields = array_merge( $fields, [ 'rc_logid', 'rc_log_type', 'rc_log_action', 'rc_params' ] );
386  }
387 
388  return $fields;
389  }
390 
392  IDatabase $db,
393  User $user,
395  ) {
396  $watchlistOwnerId = $this->getWatchlistOwnerId( $user, $options );
397  $conds = [ 'wl_user' => $watchlistOwnerId ];
398 
399  if ( !$options['allRevisions'] ) {
400  $conds[] = $db->makeList(
401  [ 'rc_this_oldid=page_latest', 'rc_type=' . RC_LOG ],
402  LIST_OR
403  );
404  }
405 
406  if ( $options['namespaceIds'] ) {
407  $conds['wl_namespace'] = array_map( 'intval', $options['namespaceIds'] );
408  }
409 
410  if ( array_key_exists( 'rcTypes', $options ) ) {
411  $conds['rc_type'] = array_map( 'intval', $options['rcTypes'] );
412  }
413 
414  $conds = array_merge(
415  $conds,
417  );
418 
419  $conds = array_merge( $conds, $this->getStartEndConds( $db, $options ) );
420 
421  if ( !isset( $options['start'] ) && !isset( $options['end'] ) ) {
422  if ( $db->getType() === 'mysql' ) {
423  // This is an index optimization for mysql
424  $conds[] = 'rc_timestamp > ' . $db->addQuotes( '' );
425  }
426  }
427 
428  $conds = array_merge( $conds, $this->getUserRelatedConds( $db, $user, $options ) );
429 
430  $deletedPageLogCond = $this->getExtraDeletedPageLogEntryRelatedCond( $db, $user );
431  if ( $deletedPageLogCond ) {
432  $conds[] = $deletedPageLogCond;
433  }
434 
435  return $conds;
436  }
437 
438  private function getWatchlistOwnerId( User $user, array $options ) {
439  if ( array_key_exists( 'watchlistOwner', $options ) ) {
441  $watchlistOwner = $options['watchlistOwner'];
442  $ownersToken = $watchlistOwner->getOption( 'watchlisttoken' );
443  $token = $options['watchlistOwnerToken'];
444  if ( $ownersToken == '' || !hash_equals( $ownersToken, $token ) ) {
445  throw ApiUsageException::newWithMessage( null, 'apierror-bad-watchlist-token', 'bad_wltoken' );
446  }
447  return $watchlistOwner->getId();
448  }
449  return $user->getId();
450  }
451 
453  $conds = [];
454 
455  if ( in_array( self::FILTER_MINOR, $options['filters'] ) ) {
456  $conds[] = 'rc_minor != 0';
457  } elseif ( in_array( self::FILTER_NOT_MINOR, $options['filters'] ) ) {
458  $conds[] = 'rc_minor = 0';
459  }
460 
461  if ( in_array( self::FILTER_BOT, $options['filters'] ) ) {
462  $conds[] = 'rc_bot != 0';
463  } elseif ( in_array( self::FILTER_NOT_BOT, $options['filters'] ) ) {
464  $conds[] = 'rc_bot = 0';
465  }
466 
467  if ( in_array( self::FILTER_ANON, $options['filters'] ) ) {
468  $conds[] = 'rc_user = 0';
469  } elseif ( in_array( self::FILTER_NOT_ANON, $options['filters'] ) ) {
470  $conds[] = 'rc_user != 0';
471  }
472 
473  if ( $user->useRCPatrol() || $user->useNPPatrol() ) {
474  // TODO: not sure if this should simply ignore patrolled filters if user does not have the patrol
475  // right, or maybe rather fail loud at this point, same as e.g. ApiQueryWatchlist does?
476  if ( in_array( self::FILTER_PATROLLED, $options['filters'] ) ) {
477  $conds[] = 'rc_patrolled != 0';
478  } elseif ( in_array( self::FILTER_NOT_PATROLLED, $options['filters'] ) ) {
479  $conds[] = 'rc_patrolled = 0';
480  }
481  }
482 
483  if ( in_array( self::FILTER_UNREAD, $options['filters'] ) ) {
484  $conds[] = 'rc_timestamp >= wl_notificationtimestamp';
485  } elseif ( in_array( self::FILTER_NOT_UNREAD, $options['filters'] ) ) {
486  // TODO: should this be changed to use Database::makeList?
487  $conds[] = 'wl_notificationtimestamp IS NULL OR rc_timestamp < wl_notificationtimestamp';
488  }
489 
490  return $conds;
491  }
492 
493  private function getStartEndConds( IDatabase $db, array $options ) {
494  if ( !isset( $options['start'] ) && !isset( $options['end'] ) ) {
495  return [];
496  }
497 
498  $conds = [];
499 
500  if ( isset( $options['start'] ) ) {
501  $after = $options['dir'] === self::DIR_OLDER ? '<=' : '>=';
502  $conds[] = 'rc_timestamp ' . $after . ' ' .
503  $db->addQuotes( $db->timestamp( $options['start'] ) );
504  }
505  if ( isset( $options['end'] ) ) {
506  $before = $options['dir'] === self::DIR_OLDER ? '>=' : '<=';
507  $conds[] = 'rc_timestamp ' . $before . ' ' .
508  $db->addQuotes( $db->timestamp( $options['end'] ) );
509  }
510 
511  return $conds;
512  }
513 
514  private function getUserRelatedConds( IDatabase $db, User $user, array $options ) {
515  if ( !array_key_exists( 'onlyByUser', $options ) && !array_key_exists( 'notByUser', $options ) ) {
516  return [];
517  }
518 
519  $conds = [];
520 
521  if ( array_key_exists( 'onlyByUser', $options ) ) {
522  $conds['rc_user_text'] = $options['onlyByUser'];
523  } elseif ( array_key_exists( 'notByUser', $options ) ) {
524  $conds[] = 'rc_user_text != ' . $db->addQuotes( $options['notByUser'] );
525  }
526 
527  // Avoid brute force searches (T19342)
528  $bitmask = 0;
529  if ( !$user->isAllowed( 'deletedhistory' ) ) {
530  $bitmask = Revision::DELETED_USER;
531  } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
533  }
534  if ( $bitmask ) {
535  $conds[] = $db->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask";
536  }
537 
538  return $conds;
539  }
540 
542  // LogPage::DELETED_ACTION hides the affected page, too. So hide those
543  // entirely from the watchlist, or someone could guess the title.
544  $bitmask = 0;
545  if ( !$user->isAllowed( 'deletedhistory' ) ) {
546  $bitmask = LogPage::DELETED_ACTION;
547  } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
549  }
550  if ( $bitmask ) {
551  return $db->makeList( [
552  'rc_type != ' . RC_LOG,
553  $db->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask",
554  ], LIST_OR );
555  }
556  return '';
557  }
558 
559  private function getStartFromConds( IDatabase $db, array $options, array $startFrom ) {
560  $op = $options['dir'] === self::DIR_OLDER ? '<' : '>';
561  list( $rcTimestamp, $rcId ) = $startFrom;
562  $rcTimestamp = $db->addQuotes( $db->timestamp( $rcTimestamp ) );
563  $rcId = (int)$rcId;
564  return $db->makeList(
565  [
566  "rc_timestamp $op $rcTimestamp",
567  $db->makeList(
568  [
569  "rc_timestamp = $rcTimestamp",
570  "rc_id $op= $rcId"
571  ],
572  LIST_AND
573  )
574  ],
575  LIST_OR
576  );
577  }
578 
580  $conds = [ 'wl_user' => $user->getId() ];
581  if ( $options['namespaceIds'] ) {
582  $conds['wl_namespace'] = array_map( 'intval', $options['namespaceIds'] );
583  }
584  if ( isset( $options['filter'] ) ) {
585  $filter = $options['filter'];
586  if ( $filter === self::FILTER_CHANGED ) {
587  $conds[] = 'wl_notificationtimestamp IS NOT NULL';
588  } else {
589  $conds[] = 'wl_notificationtimestamp IS NULL';
590  }
591  }
592 
593  if ( isset( $options['from'] ) ) {
594  $op = $options['sort'] === self::SORT_ASC ? '>' : '<';
595  $conds[] = $this->getFromUntilTargetConds( $db, $options['from'], $op );
596  }
597  if ( isset( $options['until'] ) ) {
598  $op = $options['sort'] === self::SORT_ASC ? '<' : '>';
599  $conds[] = $this->getFromUntilTargetConds( $db, $options['until'], $op );
600  }
601  if ( isset( $options['startFrom'] ) ) {
602  $op = $options['sort'] === self::SORT_ASC ? '>' : '<';
603  $conds[] = $this->getFromUntilTargetConds( $db, $options['startFrom'], $op );
604  }
605 
606  return $conds;
607  }
608 
618  private function getFromUntilTargetConds( IDatabase $db, LinkTarget $target, $op ) {
619  return $db->makeList(
620  [
621  "wl_namespace $op " . $target->getNamespace(),
622  $db->makeList(
623  [
624  'wl_namespace = ' . $target->getNamespace(),
625  "wl_title $op= " . $db->addQuotes( $target->getDBkey() )
626  ],
627  LIST_AND
628  )
629  ],
630  LIST_OR
631  );
632  }
633 
635  $dbOptions = [];
636 
637  if ( array_key_exists( 'dir', $options ) ) {
638  $sort = $options['dir'] === self::DIR_OLDER ? ' DESC' : '';
639  $dbOptions['ORDER BY'] = [ 'rc_timestamp' . $sort, 'rc_id' . $sort ];
640  }
641 
642  if ( array_key_exists( 'limit', $options ) ) {
643  $dbOptions['LIMIT'] = (int)$options['limit'] + 1;
644  }
645 
646  return $dbOptions;
647  }
648 
650  $dbOptions = [];
651  if ( array_key_exists( 'sort', $options ) ) {
652  $dbOptions['ORDER BY'] = [
653  "wl_namespace {$options['sort']}",
654  "wl_title {$options['sort']}"
655  ];
656  if ( count( $options['namespaceIds'] ) === 1 ) {
657  $dbOptions['ORDER BY'] = "wl_title {$options['sort']}";
658  }
659  }
660  if ( array_key_exists( 'limit', $options ) ) {
661  $dbOptions['LIMIT'] = (int)$options['limit'];
662  }
663  return $dbOptions;
664  }
665 
667  $joinConds = [
668  'watchlist' => [ 'INNER JOIN',
669  [
670  'wl_namespace=rc_namespace',
671  'wl_title=rc_title'
672  ]
673  ]
674  ];
675  if ( !$options['allRevisions'] ) {
676  $joinConds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
677  }
678  if ( in_array( self::INCLUDE_COMMENT, $options['includeFields'] ) ) {
679  $joinConds += $this->getCommentStore()->getJoin()['joins'];
680  }
681  return $joinConds;
682  }
683 
684 }
WatchedItemQueryService\getConnection
getConnection()
Definition: WatchedItemQueryService.php:81
Revision\DELETED_USER
const DELETED_USER
Definition: Revision.php:92
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:244
Revision\DELETED_RESTRICTED
const DELETED_RESTRICTED
Definition: Revision.php:93
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:618
RC_EXTERNAL
const RC_EXTERNAL
Definition: Defines.php:146
WatchedItemQueryService\getWatchedItemsWithRCInfoQueryConds
getWatchedItemsWithRCInfoQueryConds(IDatabase $db, User $user, array $options)
Definition: WatchedItemQueryService.php:391
WatchedItemQueryService\getWatchedItemsWithRCInfoQueryJoinConds
getWatchedItemsWithRCInfoQueryJoinConds(array $options)
Definition: WatchedItemQueryService.php:666
WatchedItemQueryServiceExtension
Definition: WatchedItemQueryServiceExtension.php:16
WatchedItemQueryService\getUserRelatedConds
getUserRelatedConds(IDatabase $db, User $user, array $options)
Definition: WatchedItemQueryService.php:514
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
$tables
this hook is for auditing only RecentChangesLinked and Watchlist 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:988
WatchedItemQueryService\INCLUDE_COMMENT
const INCLUDE_COMMENT
Definition: WatchedItemQueryService.php:26
WatchedItemQueryService\$loadBalancer
LoadBalancer $loadBalancer
Definition: WatchedItemQueryService.php:53
WatchedItemQueryService\getWatchedItemsForUserQueryConds
getWatchedItemsForUserQueryConds(IDatabase $db, User $user, array $options)
Definition: WatchedItemQueryService.php:579
Wikimedia\Rdbms\IDatabase\makeList
makeList( $a, $mode=self::LIST_COMMA)
Makes an encoded list of strings from an array.
WatchedItemQueryService\FILTER_PATROLLED
const FILTER_PATROLLED
Definition: WatchedItemQueryService.php:40
captcha-old.count
count
Definition: captcha-old.py:249
WatchedItemQueryService\getWatchedItemsWithRCInfoQueryFields
getWatchedItemsWithRCInfoQueryFields(array $options)
Definition: WatchedItemQueryService.php:341
WatchedItemQueryService\getStartEndConds
getStartEndConds(IDatabase $db, array $options)
Definition: WatchedItemQueryService.php:493
RC_LOG
const RC_LOG
Definition: Defines.php:145
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\getExtraDeletedPageLogEntryRelatedCond
getExtraDeletedPageLogEntryRelatedCond(IDatabase $db, User $user)
Definition: WatchedItemQueryService.php:541
WatchedItemQueryService\getWatchedItemsWithRCInfoQueryDbOptions
getWatchedItemsWithRCInfoQueryDbOptions(array $options)
Definition: WatchedItemQueryService.php:634
CommentStore
CommentStore handles storage of comments (edit summaries, log reasons, etc) in the database.
Definition: CommentStore.php:30
RC_EDIT
const RC_EDIT
Definition: Defines.php:143
WatchedItemQueryService\getWatchedItemsForUserQueryDbOptions
getWatchedItemsForUserQueryDbOptions(array $options)
Definition: WatchedItemQueryService.php:649
$res
$res
Definition: database.txt:21
WatchedItemQueryService\getWatchedItemsWithRecentChangeInfo
getWatchedItemsWithRecentChangeInfo(User $user, array $options=[], &$startFrom=null)
Definition: WatchedItemQueryService.php:135
ApiUsageException\newWithMessage
static newWithMessage(ApiBase $module=null, $msg, $code=null, $data=null, $httpCode=0)
Definition: ApiUsageException.php:144
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:44
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:40
WatchedItemQueryService\INCLUDE_LOG_INFO
const INCLUDE_LOG_INFO
Definition: WatchedItemQueryService.php:29
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:47
WatchedItemQueryService\getWatchedItemsWithRCInfoQueryFilterConds
getWatchedItemsWithRCInfoQueryFilterConds(User $user, array $options)
Definition: WatchedItemQueryService.php:452
WatchedItemQueryService\FILTER_MINOR
const FILTER_MINOR
Definition: WatchedItemQueryService.php:34
WatchedItemQueryService\FILTER_NOT_CHANGED
const FILTER_NOT_CHANGED
Definition: WatchedItemQueryService.php:45
WatchedItemQueryService\INCLUDE_USER
const INCLUDE_USER
Definition: WatchedItemQueryService.php:24
WatchedItemQueryService\FILTER_NOT_BOT
const FILTER_NOT_BOT
Definition: WatchedItemQueryService.php:37
WatchedItemQueryService
Definition: WatchedItemQueryService.php:18
WatchedItemQueryService\INCLUDE_PATROL_INFO
const INCLUDE_PATROL_INFO
Definition: WatchedItemQueryService.php:27
WatchedItemQueryService\FILTER_BOT
const FILTER_BOT
Definition: WatchedItemQueryService.php:36
WatchedItemQueryService\INCLUDE_USER_ID
const INCLUDE_USER_ID
Definition: WatchedItemQueryService.php:25
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
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:323
LogPage\DELETED_ACTION
const DELETED_ACTION
Definition: LogPage.php:32
WatchedItemQueryService\getCommentStore
getCommentStore()
Definition: WatchedItemQueryService.php:85
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:48
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:317
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:41
MediaWiki\Linker\LinkTarget\getDBkey
getDBkey()
Get the main part with underscores.
WatchedItemQueryService\SORT_ASC
const SORT_ASC
Definition: WatchedItemQueryService.php:47
WatchedItemQueryService\FILTER_NOT_MINOR
const FILTER_NOT_MINOR
Definition: WatchedItemQueryService.php:35
RC_NEW
const RC_NEW
Definition: Defines.php:144
WatchedItemQueryService\INCLUDE_FLAGS
const INCLUDE_FLAGS
Definition: WatchedItemQueryService.php:23
WatchedItemQueryService\__construct
__construct(LoadBalancer $loadBalancer)
Definition: WatchedItemQueryService.php:62
Wikimedia\Rdbms\IDatabase\bitAnd
bitAnd( $fieldLeft, $fieldRight)
WatchedItemQueryService\DIR_OLDER
const DIR_OLDER
Definition: WatchedItemQueryService.php:20
WatchedItemQueryService\getExtensions
getExtensions()
Definition: WatchedItemQueryService.php:69
$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:1965
Wikimedia\Rdbms\IDatabase\addQuotes
addQuotes( $s)
Adds quotes and backslashes.
WatchedItemQueryService\FILTER_CHANGED
const FILTER_CHANGED
Definition: WatchedItemQueryService.php:44
WatchedItemQueryService\FILTER_NOT_UNREAD
const FILTER_NOT_UNREAD
Definition: WatchedItemQueryService.php:43
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\getWatchlistOwnerId
getWatchlistOwnerId(User $user, array $options)
Definition: WatchedItemQueryService.php:438
WatchedItemQueryService\FILTER_NOT_ANON
const FILTER_NOT_ANON
Definition: WatchedItemQueryService.php:39
Wikimedia\Rdbms\IDatabase\getType
getType()
Get the type of the DBMS, as it appears in $wgDBtype.
WatchedItemQueryService\getWatchedItemsWithRCInfoQueryTables
getWatchedItemsWithRCInfoQueryTables(array $options)
Definition: WatchedItemQueryService.php:330
LogPage\DELETED_RESTRICTED
const DELETED_RESTRICTED
Definition: LogPage.php:35
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:147
WatchedItemQueryService\INCLUDE_SIZES
const INCLUDE_SIZES
Definition: WatchedItemQueryService.php:28
MediaWiki\Linker\LinkTarget
Definition: LinkTarget.php:27
WatchedItemQueryService\getWatchedItemsForUser
getWatchedItemsForUser(User $user, array $options=[])
For simple listing of user's watchlist items, see WatchedItemStore::getWatchedItemsForUser.
Definition: WatchedItemQueryService.php:263
WatchedItemQueryService\FILTER_ANON
const FILTER_ANON
Definition: WatchedItemQueryService.php:38
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:51
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:203
WatchedItemQueryService\FILTER_UNREAD
const FILTER_UNREAD
Definition: WatchedItemQueryService.php:42
WatchedItemQueryService\$extensions
WatchedItemQueryServiceExtension[] null $extensions
Definition: WatchedItemQueryService.php:56
WatchedItemQueryService\$commentStore
CommentStore null $commentStore
Definition: WatchedItemQueryService.php:60
array
the array() calling protocol came about after MediaWiki 1.4rc1.
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:559