60 private function run( $resultPageSet =
null ) {
68 if ( $params[
'prop'] !==
null && $resultPageSet ===
null ) {
69 $prop = array_flip( $params[
'prop'] );
71 $this->fld_ids = isset( $prop[
'ids'] );
72 $this->fld_title = isset( $prop[
'title'] );
73 $this->fld_flags = isset( $prop[
'flags'] );
74 $this->fld_user = isset( $prop[
'user'] );
75 $this->fld_userid = isset( $prop[
'userid'] );
76 $this->fld_comment = isset( $prop[
'comment'] );
77 $this->fld_parsedcomment = isset( $prop[
'parsedcomment'] );
78 $this->fld_timestamp = isset( $prop[
'timestamp'] );
79 $this->fld_sizes = isset( $prop[
'sizes'] );
80 $this->fld_patrol = isset( $prop[
'patrol'] );
81 $this->fld_notificationtimestamp = isset( $prop[
'notificationtimestamp'] );
82 $this->fld_loginfo = isset( $prop[
'loginfo'] );
83 $this->fld_tags = isset( $prop[
'tags'] );
85 if ( $this->fld_patrol && !$user->useRCPatrol() && !$user->useNPPatrol() ) {
86 $this->
dieWithError(
'apierror-permissiondenied-patrolflag',
'patrol' );
89 if ( $this->fld_comment || $this->fld_parsedcomment ) {
90 $this->commentStore = CommentStore::getStore();
95 'dir' => $params[
'dir'] ===
'older'
96 ? WatchedItemQueryService::DIR_OLDER
97 : WatchedItemQueryService::DIR_NEWER,
100 if ( $resultPageSet ===
null ) {
103 $options[
'usedInGenerator'] =
true;
106 if ( $params[
'start'] ) {
107 $options[
'start'] = $params[
'start'];
109 if ( $params[
'end'] ) {
110 $options[
'end'] = $params[
'end'];
114 if ( $params[
'continue'] !==
null ) {
115 $cont = explode(
'|', $params[
'continue'] );
117 $continueTimestamp = $cont[0];
118 $continueId = (int)$cont[1];
120 $startFrom = [ $continueTimestamp, $continueId ];
123 if ( $wlowner !== $user ) {
124 $options[
'watchlistOwner'] = $wlowner;
125 $options[
'watchlistOwnerToken'] = $params[
'token'];
128 if ( $params[
'namespace'] !==
null ) {
129 $options[
'namespaceIds'] = $params[
'namespace'];
132 if ( $params[
'allrev'] ) {
133 $options[
'allRevisions'] =
true;
136 if ( $params[
'show'] !==
null ) {
137 $show = array_flip( $params[
'show'] );
145 if ( isset( $show[WatchedItemQueryService::FILTER_PATROLLED] )
146 || isset( $show[WatchedItemQueryService::FILTER_NOT_PATROLLED] )
148 if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) {
149 $this->
dieWithError(
'apierror-permissiondenied-patrolflag',
'permissiondenied' );
153 $options[
'filters'] = array_keys( $show );
156 if ( $params[
'type'] !==
null ) {
158 $rcTypes = RecentChange::parseToRCType( $params[
'type'] );
160 $options[
'rcTypes'] = $rcTypes;
162 }
catch ( Exception $e ) {
168 if ( $params[
'user'] !==
null ) {
169 $options[
'onlyByUser'] = $params[
'user'];
171 if ( $params[
'excludeuser'] !==
null ) {
172 $options[
'notByUser'] = $params[
'excludeuser'];
175 $options[
'limit'] = $params[
'limit'];
177 $this->
getHookRunner()->onApiQueryWatchlistPrepareWatchedItemQueryServiceOptions(
178 $this, $params, $options );
181 $services = MediaWikiServices::getInstance();
182 $watchedItemQuery = $services->getWatchedItemQueryService();
183 $items = $watchedItemQuery->getWatchedItemsWithRecentChangeInfo( $wlowner, $options, $startFrom );
186 if ( $items !== [] && $resultPageSet ===
null && $this->fld_title &&
187 $services->getContentLanguage()->needsGenderDistinction()
189 $nsInfo = $services->getNamespaceInfo();
191 foreach ( $items as list( $watchedItem, $recentChangeInfo ) ) {
193 $linkTarget = $watchedItem->getLinkTarget();
194 if ( $nsInfo->hasGenderDistinction( $linkTarget->getNamespace() ) ) {
195 $usernames[] = $linkTarget->getText();
198 if ( $usernames !== [] ) {
199 $services->getGenderCache()->doQuery( $usernames, __METHOD__ );
203 foreach ( $items as list( $watchedItem, $recentChangeInfo ) ) {
205 if ( $resultPageSet ===
null ) {
209 $startFrom = [ $recentChangeInfo[
'rc_timestamp'], $recentChangeInfo[
'rc_id'] ];
212 } elseif ( $params[
'allrev'] ) {
213 $ids[] = (int)$recentChangeInfo[
'rc_this_oldid'];
215 $ids[] = (int)$recentChangeInfo[
'rc_cur_id'];
219 if ( $startFrom !==
null ) {
223 if ( $resultPageSet ===
null ) {
228 } elseif ( $params[
'allrev'] ) {
229 $resultPageSet->populateFromRevisionIDs( $ids );
231 $resultPageSet->populateFromPageIDs( $ids );
237 if ( $this->fld_flags ) {
238 $includeFields[] = WatchedItemQueryService::INCLUDE_FLAGS;
240 if ( $this->fld_user || $this->fld_userid ) {
241 $includeFields[] = WatchedItemQueryService::INCLUDE_USER_ID;
243 if ( $this->fld_user ) {
244 $includeFields[] = WatchedItemQueryService::INCLUDE_USER;
246 if ( $this->fld_comment || $this->fld_parsedcomment ) {
247 $includeFields[] = WatchedItemQueryService::INCLUDE_COMMENT;
249 if ( $this->fld_patrol ) {
250 $includeFields[] = WatchedItemQueryService::INCLUDE_PATROL_INFO;
251 $includeFields[] = WatchedItemQueryService::INCLUDE_AUTOPATROL_INFO;
253 if ( $this->fld_sizes ) {
254 $includeFields[] = WatchedItemQueryService::INCLUDE_SIZES;
256 if ( $this->fld_loginfo ) {
257 $includeFields[] = WatchedItemQueryService::INCLUDE_LOG_INFO;
259 if ( $this->fld_tags ) {
260 $includeFields[] = WatchedItemQueryService::INCLUDE_TAGS;
262 return $includeFields;
266 return ( isset( $show[WatchedItemQueryService::FILTER_MINOR] )
267 && isset( $show[WatchedItemQueryService::FILTER_NOT_MINOR] ) )
268 || ( isset( $show[WatchedItemQueryService::FILTER_BOT] )
269 && isset( $show[WatchedItemQueryService::FILTER_NOT_BOT] ) )
270 || ( isset( $show[WatchedItemQueryService::FILTER_ANON] )
271 && isset( $show[WatchedItemQueryService::FILTER_NOT_ANON] ) )
272 || ( isset( $show[WatchedItemQueryService::FILTER_PATROLLED] )
273 && isset( $show[WatchedItemQueryService::FILTER_NOT_PATROLLED] ) )
274 || ( isset( $show[WatchedItemQueryService::FILTER_AUTOPATROLLED] )
275 && isset( $show[WatchedItemQueryService::FILTER_NOT_AUTOPATROLLED] ) )
276 || ( isset( $show[WatchedItemQueryService::FILTER_AUTOPATROLLED] )
277 && isset( $show[WatchedItemQueryService::FILTER_NOT_PATROLLED] ) )
278 || ( isset( $show[WatchedItemQueryService::FILTER_UNREAD] )
279 && isset( $show[WatchedItemQueryService::FILTER_NOT_UNREAD] ) );
289 $type = (int)$recentChangeInfo[
'rc_type'];
290 $vals[
'type'] = RecentChange::parseFromRCType(
$type );
294 if ( $this->fld_title || $this->fld_ids ) {
297 $vals[
'actionhidden'] =
true;
301 LogEventsList::userCanBitfield(
302 $recentChangeInfo[
'rc_deleted'],
307 if ( $this->fld_title ) {
310 if ( $this->fld_ids ) {
311 $vals[
'pageid'] = (int)$recentChangeInfo[
'rc_cur_id'];
312 $vals[
'revid'] = (int)$recentChangeInfo[
'rc_this_oldid'];
313 $vals[
'old_revid'] = (int)$recentChangeInfo[
'rc_last_oldid'];
319 if ( $this->fld_user || $this->fld_userid ) {
320 if ( $recentChangeInfo[
'rc_deleted'] & RevisionRecord::DELETED_USER ) {
321 $vals[
'userhidden'] =
true;
324 if ( RevisionRecord::userCanBitfield(
325 $recentChangeInfo[
'rc_deleted'],
326 RevisionRecord::DELETED_USER,
329 if ( $this->fld_userid ) {
330 $vals[
'userid'] = (int)$recentChangeInfo[
'rc_user'];
332 $vals[
'user'] = (int)$recentChangeInfo[
'rc_user'];
335 if ( $this->fld_user ) {
336 $vals[
'user'] = $recentChangeInfo[
'rc_user_text'];
339 if ( !$recentChangeInfo[
'rc_user'] ) {
340 $vals[
'anon'] =
true;
346 if ( $this->fld_flags ) {
347 $vals[
'bot'] = (bool)$recentChangeInfo[
'rc_bot'];
348 $vals[
'new'] = $recentChangeInfo[
'rc_type'] ==
RC_NEW;
349 $vals[
'minor'] = (bool)$recentChangeInfo[
'rc_minor'];
353 if ( $this->fld_sizes ) {
354 $vals[
'oldlen'] = (int)$recentChangeInfo[
'rc_old_len'];
355 $vals[
'newlen'] = (int)$recentChangeInfo[
'rc_new_len'];
359 if ( $this->fld_timestamp ) {
360 $vals[
'timestamp'] =
wfTimestamp( TS_ISO_8601, $recentChangeInfo[
'rc_timestamp'] );
363 if ( $this->fld_notificationtimestamp ) {
370 if ( $this->fld_comment || $this->fld_parsedcomment ) {
371 if ( $recentChangeInfo[
'rc_deleted'] & RevisionRecord::DELETED_COMMENT ) {
372 $vals[
'commenthidden'] =
true;
375 if ( RevisionRecord::userCanBitfield(
376 $recentChangeInfo[
'rc_deleted'],
377 RevisionRecord::DELETED_COMMENT,
380 $comment = $this->commentStore->getComment(
'rc_comment', $recentChangeInfo )->text;
381 if ( $this->fld_comment ) {
382 $vals[
'comment'] = $comment;
385 if ( $this->fld_parsedcomment ) {
392 if ( $this->fld_patrol ) {
393 $vals[
'patrolled'] = $recentChangeInfo[
'rc_patrolled'] != RecentChange::PRC_UNPATROLLED;
394 $vals[
'unpatrolled'] = ChangesList::isUnpatrolled( (
object)$recentChangeInfo, $user );
395 $vals[
'autopatrolled'] = $recentChangeInfo[
'rc_patrolled'] == RecentChange::PRC_AUTOPATROLLED;
398 if ( $this->fld_loginfo && $recentChangeInfo[
'rc_type'] ==
RC_LOG ) {
400 $vals[
'actionhidden'] =
true;
403 if ( LogEventsList::userCanBitfield(
404 $recentChangeInfo[
'rc_deleted'],
408 $vals[
'logid'] = (int)$recentChangeInfo[
'rc_logid'];
409 $vals[
'logtype'] = $recentChangeInfo[
'rc_log_type'];
410 $vals[
'logaction'] = $recentChangeInfo[
'rc_log_action'];
415 if ( $this->fld_tags ) {
416 if ( $recentChangeInfo[
'rc_tags'] ) {
417 $tags = explode(
',', $recentChangeInfo[
'rc_tags'] );
418 ApiResult::setIndexedTagName( $tags,
'tag' );
419 $vals[
'tags'] = $tags;
425 if ( $anyHidden && ( $recentChangeInfo[
'rc_deleted'] & RevisionRecord::DELETED_RESTRICTED ) ) {
426 $vals[
'suppressed'] =
true;
429 $this->
getHookRunner()->onApiQueryWatchlistExtractOutputData(
430 $this, $watchedItem, $recentChangeInfo, $vals );
450 UserDef::PARAM_ALLOWED_USER_TYPES => [
'name',
'ip',
'id',
'interwiki' ],
454 UserDef::PARAM_ALLOWED_USER_TYPES => [
'name',
'ip',
'id',
'interwiki' ],
486 'notificationtimestamp',
494 WatchedItemQueryService::FILTER_MINOR,
495 WatchedItemQueryService::FILTER_NOT_MINOR,
496 WatchedItemQueryService::FILTER_BOT,
497 WatchedItemQueryService::FILTER_NOT_BOT,
498 WatchedItemQueryService::FILTER_ANON,
499 WatchedItemQueryService::FILTER_NOT_ANON,
500 WatchedItemQueryService::FILTER_PATROLLED,
501 WatchedItemQueryService::FILTER_NOT_PATROLLED,
502 WatchedItemQueryService::FILTER_AUTOPATROLLED,
503 WatchedItemQueryService::FILTER_NOT_AUTOPATROLLED,
504 WatchedItemQueryService::FILTER_UNREAD,
505 WatchedItemQueryService::FILTER_NOT_UNREAD,
516 UserDef::PARAM_ALLOWED_USER_TYPES => [
'name' ],