Completed
Branch master (9259dd)
by
unknown
27:26
created

ApiQueryRecentChanges::run()   F

Complexity

Conditions 65
Paths > 20000

Size

Total Lines 271
Code Lines 171

Duplication

Lines 83
Ratio 30.63 %

Importance

Changes 0
Metric Value
cc 65
eloc 171
nc 355622400
nop 1
dl 83
loc 271
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 *
4
 *
5
 * Created on Oct 19, 2006
6
 *
7
 * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License along
20
 * with this program; if not, write to the Free Software Foundation, Inc.,
21
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
 * http://www.gnu.org/copyleft/gpl.html
23
 *
24
 * @file
25
 */
26
27
/**
28
 * A query action to enumerate the recent changes that were done to the wiki.
29
 * Various filters are supported.
30
 *
31
 * @ingroup API
32
 */
33
class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
34
35
	public function __construct( ApiQuery $query, $moduleName ) {
36
		parent::__construct( $query, $moduleName, 'rc' );
37
	}
38
39
	private $fld_comment = false, $fld_parsedcomment = false, $fld_user = false, $fld_userid = false,
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
40
		$fld_flags = false, $fld_timestamp = false, $fld_title = false, $fld_ids = false,
41
		$fld_sizes = false, $fld_redirect = false, $fld_patrolled = false, $fld_loginfo = false,
42
		$fld_tags = false, $fld_sha1 = false, $token = [];
43
44
	private $tokenFunctions;
45
46
	/**
47
	 * Get an array mapping token names to their handler functions.
48
	 * The prototype for a token function is func($pageid, $title, $rc)
49
	 * it should return a token or false (permission denied)
50
	 * @deprecated since 1.24
51
	 * @return array Array(tokenname => function)
52
	 */
53 View Code Duplication
	protected function getTokenFunctions() {
54
		// Don't call the hooks twice
55
		if ( isset( $this->tokenFunctions ) ) {
56
			return $this->tokenFunctions;
57
		}
58
59
		// If we're in a mode that breaks the same-origin policy, no tokens can
60
		// be obtained
61
		if ( $this->lacksSameOriginSecurity() ) {
62
			return [];
63
		}
64
65
		$this->tokenFunctions = [
66
			'patrol' => [ 'ApiQueryRecentChanges', 'getPatrolToken' ]
67
		];
68
		Hooks::run( 'APIQueryRecentChangesTokens', [ &$this->tokenFunctions ] );
69
70
		return $this->tokenFunctions;
71
	}
72
73
	/**
74
	 * @deprecated since 1.24
75
	 * @param int $pageid
76
	 * @param Title $title
77
	 * @param RecentChange|null $rc
78
	 * @return bool|string
79
	 */
80
	public static function getPatrolToken( $pageid, $title, $rc = null ) {
81
		global $wgUser;
82
83
		$validTokenUser = false;
84
85
		if ( $rc ) {
86
			if ( ( $wgUser->useRCPatrol() && $rc->getAttribute( 'rc_type' ) == RC_EDIT ) ||
87
				( $wgUser->useNPPatrol() && $rc->getAttribute( 'rc_type' ) == RC_NEW )
88
			) {
89
				$validTokenUser = true;
90
			}
91
		} elseif ( $wgUser->useRCPatrol() || $wgUser->useNPPatrol() ) {
92
			$validTokenUser = true;
93
		}
94
95
		if ( $validTokenUser ) {
96
			// The patrol token is always the same, let's exploit that
97
			static $cachedPatrolToken = null;
98
99
			if ( is_null( $cachedPatrolToken ) ) {
100
				$cachedPatrolToken = $wgUser->getEditToken( 'patrol' );
101
			}
102
103
			return $cachedPatrolToken;
104
		}
105
106
		return false;
107
	}
108
109
	/**
110
	 * Sets internal state to include the desired properties in the output.
111
	 * @param array $prop Associative array of properties, only keys are used here
112
	 */
113
	public function initProperties( $prop ) {
114
		$this->fld_comment = isset( $prop['comment'] );
115
		$this->fld_parsedcomment = isset( $prop['parsedcomment'] );
116
		$this->fld_user = isset( $prop['user'] );
117
		$this->fld_userid = isset( $prop['userid'] );
118
		$this->fld_flags = isset( $prop['flags'] );
119
		$this->fld_timestamp = isset( $prop['timestamp'] );
120
		$this->fld_title = isset( $prop['title'] );
121
		$this->fld_ids = isset( $prop['ids'] );
122
		$this->fld_sizes = isset( $prop['sizes'] );
123
		$this->fld_redirect = isset( $prop['redirect'] );
124
		$this->fld_patrolled = isset( $prop['patrolled'] );
125
		$this->fld_loginfo = isset( $prop['loginfo'] );
126
		$this->fld_tags = isset( $prop['tags'] );
127
		$this->fld_sha1 = isset( $prop['sha1'] );
128
	}
129
130
	public function execute() {
131
		$this->run();
132
	}
133
134
	public function executeGenerator( $resultPageSet ) {
135
		$this->run( $resultPageSet );
136
	}
137
138
	/**
139
	 * Generates and outputs the result of this query based upon the provided parameters.
140
	 *
141
	 * @param ApiPageSet $resultPageSet
142
	 */
143
	public function run( $resultPageSet = null ) {
144
		$user = $this->getUser();
145
		/* Get the parameters of the request. */
146
		$params = $this->extractRequestParams();
147
148
		/* Build our basic query. Namely, something along the lines of:
149
		 * SELECT * FROM recentchanges WHERE rc_timestamp > $start
150
		 * 		AND rc_timestamp < $end AND rc_namespace = $namespace
151
		 */
152
		$this->addTables( 'recentchanges' );
153
		$index = [ 'recentchanges' => 'rc_timestamp' ]; // May change
154
		$this->addTimestampWhereRange( 'rc_timestamp', $params['dir'], $params['start'], $params['end'] );
155
156 View Code Duplication
		if ( !is_null( $params['continue'] ) ) {
157
			$cont = explode( '|', $params['continue'] );
158
			$this->dieContinueUsageIf( count( $cont ) != 2 );
159
			$db = $this->getDB();
160
			$timestamp = $db->addQuotes( $db->timestamp( $cont[0] ) );
0 ignored issues
show
Security Bug introduced by
It seems like $db->timestamp($cont[0]) targeting DatabaseBase::timestamp() can also be of type false; however, DatabaseBase::addQuotes() does only seem to accept string|object<Blob>, did you maybe forget to handle an error condition?
Loading history...
161
			$id = intval( $cont[1] );
162
			$this->dieContinueUsageIf( $id != $cont[1] );
163
			$op = $params['dir'] === 'older' ? '<' : '>';
164
			$this->addWhere(
165
				"rc_timestamp $op $timestamp OR " .
166
				"(rc_timestamp = $timestamp AND " .
167
				"rc_id $op= $id)"
168
			);
169
		}
170
171
		$order = $params['dir'] === 'older' ? 'DESC' : 'ASC';
172
		$this->addOption( 'ORDER BY', [
173
			"rc_timestamp $order",
174
			"rc_id $order",
175
		] );
176
177
		$this->addWhereFld( 'rc_namespace', $params['namespace'] );
178
179 View Code Duplication
		if ( !is_null( $params['type'] ) ) {
180
			try {
181
				$this->addWhereFld( 'rc_type', RecentChange::parseToRCType( $params['type'] ) );
182
			} catch ( Exception $e ) {
183
				ApiBase::dieDebug( __METHOD__, $e->getMessage() );
184
			}
185
		}
186
187
		if ( !is_null( $params['show'] ) ) {
188
			$show = array_flip( $params['show'] );
189
190
			/* Check for conflicting parameters. */
191
			if ( ( isset( $show['minor'] ) && isset( $show['!minor'] ) )
192
				|| ( isset( $show['bot'] ) && isset( $show['!bot'] ) )
193
				|| ( isset( $show['anon'] ) && isset( $show['!anon'] ) )
194
				|| ( isset( $show['redirect'] ) && isset( $show['!redirect'] ) )
195
				|| ( isset( $show['patrolled'] ) && isset( $show['!patrolled'] ) )
196
				|| ( isset( $show['patrolled'] ) && isset( $show['unpatrolled'] ) )
197
				|| ( isset( $show['!patrolled'] ) && isset( $show['unpatrolled'] ) )
198
			) {
199
				$this->dieUsageMsg( 'show' );
200
			}
201
202
			// Check permissions
203 View Code Duplication
			if ( isset( $show['patrolled'] )
204
				|| isset( $show['!patrolled'] )
205
				|| isset( $show['unpatrolled'] )
206
			) {
207
				if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) {
208
					$this->dieUsage(
209
						'You need patrol or patrolmarks permission to request the patrolled flag',
210
						'permissiondenied'
211
					);
212
				}
213
			}
214
215
			/* Add additional conditions to query depending upon parameters. */
216
			$this->addWhereIf( 'rc_minor = 0', isset( $show['!minor'] ) );
217
			$this->addWhereIf( 'rc_minor != 0', isset( $show['minor'] ) );
218
			$this->addWhereIf( 'rc_bot = 0', isset( $show['!bot'] ) );
219
			$this->addWhereIf( 'rc_bot != 0', isset( $show['bot'] ) );
220
			$this->addWhereIf( 'rc_user = 0', isset( $show['anon'] ) );
221
			$this->addWhereIf( 'rc_user != 0', isset( $show['!anon'] ) );
222
			$this->addWhereIf( 'rc_patrolled = 0', isset( $show['!patrolled'] ) );
223
			$this->addWhereIf( 'rc_patrolled != 0', isset( $show['patrolled'] ) );
224
			$this->addWhereIf( 'page_is_redirect = 1', isset( $show['redirect'] ) );
225
226
			if ( isset( $show['unpatrolled'] ) ) {
227
				// See ChangesList::isUnpatrolled
228
				if ( $user->useRCPatrol() ) {
229
					$this->addWhere( 'rc_patrolled = 0' );
230
				} elseif ( $user->useNPPatrol() ) {
231
					$this->addWhere( 'rc_patrolled = 0' );
232
					$this->addWhereFld( 'rc_type', RC_NEW );
233
				}
234
			}
235
236
			// Don't throw log entries out the window here
237
			$this->addWhereIf(
238
				'page_is_redirect = 0 OR page_is_redirect IS NULL',
239
				isset( $show['!redirect'] )
240
			);
241
		}
242
243 View Code Duplication
		if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) {
244
			$this->dieUsage( 'user and excludeuser cannot be used together', 'user-excludeuser' );
245
		}
246
247
		if ( !is_null( $params['user'] ) ) {
248
			$this->addWhereFld( 'rc_user_text', $params['user'] );
249
			$index['recentchanges'] = 'rc_user_text';
250
		}
251
252 View Code Duplication
		if ( !is_null( $params['excludeuser'] ) ) {
253
			// We don't use the rc_user_text index here because
254
			// * it would require us to sort by rc_user_text before rc_timestamp
255
			// * the != condition doesn't throw out too many rows anyway
256
			$this->addWhere( 'rc_user_text != ' . $this->getDB()->addQuotes( $params['excludeuser'] ) );
257
		}
258
259
		/* Add the fields we're concerned with to our query. */
260
		$this->addFields( [
261
			'rc_id',
262
			'rc_timestamp',
263
			'rc_namespace',
264
			'rc_title',
265
			'rc_cur_id',
266
			'rc_type',
267
			'rc_deleted'
268
		] );
269
270
		$showRedirects = false;
271
		/* Determine what properties we need to display. */
272
		if ( !is_null( $params['prop'] ) ) {
273
			$prop = array_flip( $params['prop'] );
274
275
			/* Set up internal members based upon params. */
276
			$this->initProperties( $prop );
277
278
			if ( $this->fld_patrolled && !$user->useRCPatrol() && !$user->useNPPatrol() ) {
279
				$this->dieUsage(
280
					'You need patrol or patrolmarks permission to request the patrolled flag',
281
					'permissiondenied'
282
				);
283
			}
284
285
			/* Add fields to our query if they are specified as a needed parameter. */
286
			$this->addFieldsIf( [ 'rc_this_oldid', 'rc_last_oldid' ], $this->fld_ids );
287
			$this->addFieldsIf( 'rc_comment', $this->fld_comment || $this->fld_parsedcomment );
288
			$this->addFieldsIf( 'rc_user', $this->fld_user || $this->fld_userid );
289
			$this->addFieldsIf( 'rc_user_text', $this->fld_user );
290
			$this->addFieldsIf( [ 'rc_minor', 'rc_type', 'rc_bot' ], $this->fld_flags );
291
			$this->addFieldsIf( [ 'rc_old_len', 'rc_new_len' ], $this->fld_sizes );
292
			$this->addFieldsIf( [ 'rc_patrolled', 'rc_log_type' ], $this->fld_patrolled );
293
			$this->addFieldsIf(
294
				[ 'rc_logid', 'rc_log_type', 'rc_log_action', 'rc_params' ],
295
				$this->fld_loginfo
296
			);
297
			$showRedirects = $this->fld_redirect || isset( $show['redirect'] )
298
				|| isset( $show['!redirect'] );
299
		}
300
		$this->addFieldsIf( [ 'rc_this_oldid' ],
301
			$resultPageSet && $params['generaterevisions'] );
302
303 View Code Duplication
		if ( $this->fld_tags ) {
304
			$this->addTables( 'tag_summary' );
305
			$this->addJoinConds( [ 'tag_summary' => [ 'LEFT JOIN', [ 'rc_id=ts_rc_id' ] ] ] );
306
			$this->addFields( 'ts_tags' );
307
		}
308
309
		if ( $this->fld_sha1 ) {
310
			$this->addTables( 'revision' );
311
			$this->addJoinConds( [ 'revision' => [ 'LEFT JOIN',
312
				[ 'rc_this_oldid=rev_id' ] ] ] );
313
			$this->addFields( [ 'rev_sha1', 'rev_deleted' ] );
314
		}
315
316
		if ( $params['toponly'] || $showRedirects ) {
317
			$this->addTables( 'page' );
318
			$this->addJoinConds( [ 'page' => [ 'LEFT JOIN',
319
				[ 'rc_namespace=page_namespace', 'rc_title=page_title' ] ] ] );
320
			$this->addFields( 'page_is_redirect' );
321
322
			if ( $params['toponly'] ) {
323
				$this->addWhere( 'rc_this_oldid = page_latest' );
324
			}
325
		}
326
327 View Code Duplication
		if ( !is_null( $params['tag'] ) ) {
328
			$this->addTables( 'change_tag' );
329
			$this->addJoinConds( [ 'change_tag' => [ 'INNER JOIN', [ 'rc_id=ct_rc_id' ] ] ] );
330
			$this->addWhereFld( 'ct_tag', $params['tag'] );
331
		}
332
333
		// Paranoia: avoid brute force searches (bug 17342)
334 View Code Duplication
		if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) {
335
			if ( !$user->isAllowed( 'deletedhistory' ) ) {
336
				$bitmask = Revision::DELETED_USER;
337
			} elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
338
				$bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
339
			} else {
340
				$bitmask = 0;
341
			}
342
			if ( $bitmask ) {
343
				$this->addWhere( $this->getDB()->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask" );
344
			}
345
		}
346
		if ( $this->getRequest()->getCheck( 'namespace' ) ) {
347
			// LogPage::DELETED_ACTION hides the affected page, too.
348
			if ( !$user->isAllowed( 'deletedhistory' ) ) {
349
				$bitmask = LogPage::DELETED_ACTION;
350
			} elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
351
				$bitmask = LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED;
352
			} else {
353
				$bitmask = 0;
354
			}
355 View Code Duplication
			if ( $bitmask ) {
356
				$this->addWhere( $this->getDB()->makeList( [
357
					'rc_type != ' . RC_LOG,
358
					$this->getDB()->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask",
359
				], LIST_OR ) );
360
			}
361
		}
362
363
		$this->token = $params['token'];
364
		$this->addOption( 'LIMIT', $params['limit'] + 1 );
365
		$this->addOption( 'USE INDEX', $index );
366
367
		$count = 0;
368
		/* Perform the actual query. */
369
		$res = $this->select( __METHOD__ );
370
371
		$revids = [];
372
		$titles = [];
373
374
		$result = $this->getResult();
375
376
		/* Iterate through the rows, adding data extracted from them to our query result. */
377
		foreach ( $res as $row ) {
378 View Code Duplication
			if ( ++$count > $params['limit'] ) {
379
				// We've reached the one extra which shows that there are
380
				// additional pages to be had. Stop here...
381
				$this->setContinueEnumParameter( 'continue', "$row->rc_timestamp|$row->rc_id" );
382
				break;
383
			}
384
385
			if ( is_null( $resultPageSet ) ) {
386
				/* Extract the data from a single row. */
387
				$vals = $this->extractRowInfo( $row );
388
389
				/* Add that row's data to our final output. */
390
				$fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $vals );
391
				if ( !$fit ) {
392
					$this->setContinueEnumParameter( 'continue', "$row->rc_timestamp|$row->rc_id" );
393
					break;
394
				}
395
			} elseif ( $params['generaterevisions'] ) {
396
				$revid = (int)$row->rc_this_oldid;
397
				if ( $revid > 0 ) {
398
					$revids[] = $revid;
399
				}
400
			} else {
401
				$titles[] = Title::makeTitle( $row->rc_namespace, $row->rc_title );
402
			}
403
		}
404
405 View Code Duplication
		if ( is_null( $resultPageSet ) ) {
406
			/* Format the result */
407
			$result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'rc' );
408
		} elseif ( $params['generaterevisions'] ) {
409
			$resultPageSet->populateFromRevisionIDs( $revids );
410
		} else {
411
			$resultPageSet->populateFromTitles( $titles );
412
		}
413
	}
414
415
	/**
416
	 * Extracts from a single sql row the data needed to describe one recent change.
417
	 *
418
	 * @param stdClass $row The row from which to extract the data.
419
	 * @return array An array mapping strings (descriptors) to their respective string values.
420
	 * @access public
421
	 */
422
	public function extractRowInfo( $row ) {
423
		/* Determine the title of the page that has been changed. */
424
		$title = Title::makeTitle( $row->rc_namespace, $row->rc_title );
425
		$user = $this->getUser();
426
427
		/* Our output data. */
428
		$vals = [];
429
430
		$type = intval( $row->rc_type );
431
		$vals['type'] = RecentChange::parseFromRCType( $type );
432
433
		$anyHidden = false;
434
435
		/* Create a new entry in the result for the title. */
436 View Code Duplication
		if ( $this->fld_title || $this->fld_ids ) {
437
			if ( $type === RC_LOG && ( $row->rc_deleted & LogPage::DELETED_ACTION ) ) {
438
				$vals['actionhidden'] = true;
439
				$anyHidden = true;
440
			}
441
			if ( $type !== RC_LOG ||
442
				LogEventsList::userCanBitfield( $row->rc_deleted, LogPage::DELETED_ACTION, $user )
443
			) {
444
				if ( $this->fld_title ) {
445
					ApiQueryBase::addTitleInfo( $vals, $title );
446
				}
447
				if ( $this->fld_ids ) {
448
					$vals['pageid'] = intval( $row->rc_cur_id );
449
					$vals['revid'] = intval( $row->rc_this_oldid );
450
					$vals['old_revid'] = intval( $row->rc_last_oldid );
451
				}
452
			}
453
		}
454
455
		if ( $this->fld_ids ) {
456
			$vals['rcid'] = intval( $row->rc_id );
457
		}
458
459
		/* Add user data and 'anon' flag, if user is anonymous. */
460 View Code Duplication
		if ( $this->fld_user || $this->fld_userid ) {
461
			if ( $row->rc_deleted & Revision::DELETED_USER ) {
462
				$vals['userhidden'] = true;
463
				$anyHidden = true;
464
			}
465
			if ( Revision::userCanBitfield( $row->rc_deleted, Revision::DELETED_USER, $user ) ) {
466
				if ( $this->fld_user ) {
467
					$vals['user'] = $row->rc_user_text;
468
				}
469
470
				if ( $this->fld_userid ) {
471
					$vals['userid'] = (int)$row->rc_user;
472
				}
473
474
				if ( !$row->rc_user ) {
475
					$vals['anon'] = true;
476
				}
477
			}
478
		}
479
480
		/* Add flags, such as new, minor, bot. */
481 View Code Duplication
		if ( $this->fld_flags ) {
482
			$vals['bot'] = (bool)$row->rc_bot;
483
			$vals['new'] = $row->rc_type == RC_NEW;
484
			$vals['minor'] = (bool)$row->rc_minor;
485
		}
486
487
		/* Add sizes of each revision. (Only available on 1.10+) */
488 View Code Duplication
		if ( $this->fld_sizes ) {
489
			$vals['oldlen'] = intval( $row->rc_old_len );
490
			$vals['newlen'] = intval( $row->rc_new_len );
491
		}
492
493
		/* Add the timestamp. */
494
		if ( $this->fld_timestamp ) {
495
			$vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->rc_timestamp );
496
		}
497
498
		/* Add edit summary / log summary. */
499 View Code Duplication
		if ( $this->fld_comment || $this->fld_parsedcomment ) {
500
			if ( $row->rc_deleted & Revision::DELETED_COMMENT ) {
501
				$vals['commenthidden'] = true;
502
				$anyHidden = true;
503
			}
504
			if ( Revision::userCanBitfield( $row->rc_deleted, Revision::DELETED_COMMENT, $user ) ) {
505
				if ( $this->fld_comment && isset( $row->rc_comment ) ) {
506
					$vals['comment'] = $row->rc_comment;
507
				}
508
509
				if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) {
510
					$vals['parsedcomment'] = Linker::formatComment( $row->rc_comment, $title );
511
				}
512
			}
513
		}
514
515
		if ( $this->fld_redirect ) {
516
			$vals['redirect'] = (bool)$row->page_is_redirect;
517
		}
518
519
		/* Add the patrolled flag */
520 View Code Duplication
		if ( $this->fld_patrolled ) {
521
			$vals['patrolled'] = $row->rc_patrolled == 1;
522
			$vals['unpatrolled'] = ChangesList::isUnpatrolled( $row, $user );
523
		}
524
525 View Code Duplication
		if ( $this->fld_loginfo && $row->rc_type == RC_LOG ) {
526
			if ( $row->rc_deleted & LogPage::DELETED_ACTION ) {
527
				$vals['actionhidden'] = true;
528
				$anyHidden = true;
529
			}
530
			if ( LogEventsList::userCanBitfield( $row->rc_deleted, LogPage::DELETED_ACTION, $user ) ) {
531
				$vals['logid'] = intval( $row->rc_logid );
532
				$vals['logtype'] = $row->rc_log_type;
533
				$vals['logaction'] = $row->rc_log_action;
534
				$vals['logparams'] = LogFormatter::newFromRow( $row )->formatParametersForApi();
535
			}
536
		}
537
538 View Code Duplication
		if ( $this->fld_tags ) {
539
			if ( $row->ts_tags ) {
540
				$tags = explode( ',', $row->ts_tags );
541
				ApiResult::setIndexedTagName( $tags, 'tag' );
542
				$vals['tags'] = $tags;
543
			} else {
544
				$vals['tags'] = [];
545
			}
546
		}
547
548
		if ( $this->fld_sha1 && $row->rev_sha1 !== null ) {
549
			if ( $row->rev_deleted & Revision::DELETED_TEXT ) {
550
				$vals['sha1hidden'] = true;
551
				$anyHidden = true;
552
			}
553 View Code Duplication
			if ( Revision::userCanBitfield( $row->rev_deleted, Revision::DELETED_TEXT, $user ) ) {
554
				if ( $row->rev_sha1 !== '' ) {
555
					$vals['sha1'] = Wikimedia\base_convert( $row->rev_sha1, 36, 16, 40 );
556
				} else {
557
					$vals['sha1'] = '';
558
				}
559
			}
560
		}
561
562
		if ( !is_null( $this->token ) ) {
563
			$tokenFunctions = $this->getTokenFunctions();
0 ignored issues
show
Deprecated Code introduced by
The method ApiQueryRecentChanges::getTokenFunctions() has been deprecated with message: since 1.24

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
564
			foreach ( $this->token as $t ) {
565
				$val = call_user_func( $tokenFunctions[$t], $row->rc_cur_id,
566
					$title, RecentChange::newFromRow( $row ) );
567
				if ( $val === false ) {
568
					$this->setWarning( "Action '$t' is not allowed for the current user" );
569
				} else {
570
					$vals[$t . 'token'] = $val;
571
				}
572
			}
573
		}
574
575
		if ( $anyHidden && ( $row->rc_deleted & Revision::DELETED_RESTRICTED ) ) {
576
			$vals['suppressed'] = true;
577
		}
578
579
		return $vals;
580
	}
581
582
	public function getCacheMode( $params ) {
583
		if ( isset( $params['show'] ) ) {
584
			foreach ( $params['show'] as $show ) {
585
				if ( $show === 'patrolled' || $show === '!patrolled' ) {
586
					return 'private';
587
				}
588
			}
589
		}
590
		if ( isset( $params['token'] ) ) {
591
			return 'private';
592
		}
593
		if ( $this->userCanSeeRevDel() ) {
594
			return 'private';
595
		}
596 View Code Duplication
		if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) {
597
			// formatComment() calls wfMessage() among other things
598
			return 'anon-public-user-private';
599
		}
600
601
		return 'public';
602
	}
603
604
	public function getAllowedParams() {
605
		return [
606
			'start' => [
607
				ApiBase::PARAM_TYPE => 'timestamp'
608
			],
609
			'end' => [
610
				ApiBase::PARAM_TYPE => 'timestamp'
611
			],
612
			'dir' => [
613
				ApiBase::PARAM_DFLT => 'older',
614
				ApiBase::PARAM_TYPE => [
615
					'newer',
616
					'older'
617
				],
618
				ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
619
			],
620
			'namespace' => [
621
				ApiBase::PARAM_ISMULTI => true,
622
				ApiBase::PARAM_TYPE => 'namespace'
623
			],
624
			'user' => [
625
				ApiBase::PARAM_TYPE => 'user'
626
			],
627
			'excludeuser' => [
628
				ApiBase::PARAM_TYPE => 'user'
629
			],
630
			'tag' => null,
631
			'prop' => [
632
				ApiBase::PARAM_ISMULTI => true,
633
				ApiBase::PARAM_DFLT => 'title|timestamp|ids',
634
				ApiBase::PARAM_TYPE => [
635
					'user',
636
					'userid',
637
					'comment',
638
					'parsedcomment',
639
					'flags',
640
					'timestamp',
641
					'title',
642
					'ids',
643
					'sizes',
644
					'redirect',
645
					'patrolled',
646
					'loginfo',
647
					'tags',
648
					'sha1',
649
				],
650
				ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
651
			],
652
			'token' => [
653
				ApiBase::PARAM_DEPRECATED => true,
654
				ApiBase::PARAM_TYPE => array_keys( $this->getTokenFunctions() ),
0 ignored issues
show
Deprecated Code introduced by
The method ApiQueryRecentChanges::getTokenFunctions() has been deprecated with message: since 1.24

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
655
				ApiBase::PARAM_ISMULTI => true
656
			],
657
			'show' => [
658
				ApiBase::PARAM_ISMULTI => true,
659
				ApiBase::PARAM_TYPE => [
660
					'minor',
661
					'!minor',
662
					'bot',
663
					'!bot',
664
					'anon',
665
					'!anon',
666
					'redirect',
667
					'!redirect',
668
					'patrolled',
669
					'!patrolled',
670
					'unpatrolled'
671
				]
672
			],
673
			'limit' => [
674
				ApiBase::PARAM_DFLT => 10,
675
				ApiBase::PARAM_TYPE => 'limit',
676
				ApiBase::PARAM_MIN => 1,
677
				ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
678
				ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
679
			],
680
			'type' => [
681
				ApiBase::PARAM_DFLT => 'edit|new|log|categorize',
682
				ApiBase::PARAM_ISMULTI => true,
683
				ApiBase::PARAM_TYPE => RecentChange::getChangeTypes()
684
			],
685
			'toponly' => false,
686
			'continue' => [
687
				ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
688
			],
689
			'generaterevisions' => false,
690
		];
691
	}
692
693
	protected function getExamplesMessages() {
694
		return [
695
			'action=query&list=recentchanges'
696
				=> 'apihelp-query+recentchanges-example-simple',
697
			'action=query&generator=recentchanges&grcshow=!patrolled&prop=info'
698
				=> 'apihelp-query+recentchanges-example-generator',
699
		];
700
	}
701
702
	public function getHelpUrls() {
703
		return 'https://www.mediawiki.org/wiki/API:Recentchanges';
704
	}
705
}
706