Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 187 |
|
0.00% |
0 / 26 |
CRAP | |
0.00% |
0 / 1 |
LogPager | |
0.00% |
0 / 186 |
|
0.00% |
0 / 26 |
6320 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
2 | |||
getDefaultQuery | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
limitFilterTypes | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
30 | |||
getFilterParams | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
20 | |||
limitType | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
132 | |||
limitPerformer | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
limitTitle | |
0.00% |
0 / 35 |
|
0.00% |
0 / 1 |
132 | |||
limitAction | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
30 | |||
limitLogId | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getQueryInfo | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
110 | |||
hasEqualsClause | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
getIndexField | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
doBatchLookups | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
formatRow | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPerformer | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPage | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPattern | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getYear | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMonth | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDay | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTagFilter | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTagInvert | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getAction | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
enforceActionRestrictions | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 | |||
enforcePerformerRestrictions | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 |
1 | <?php |
2 | /** |
3 | * Contain classes to list log entries |
4 | * |
5 | * Copyright © 2004 Brooke Vibber <bvibber@wikimedia.org> |
6 | * https://www.mediawiki.org/ |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. |
12 | * |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU General Public License along |
19 | * with this program; if not, write to the Free Software Foundation, Inc., |
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
21 | * http://www.gnu.org/copyleft/gpl.html |
22 | * |
23 | * @file |
24 | */ |
25 | |
26 | namespace MediaWiki\Pager; |
27 | |
28 | use DatabaseLogEntry; |
29 | use LogEventsList; |
30 | use LogFormatterFactory; |
31 | use LogPage; |
32 | use MediaWiki\Cache\LinkBatchFactory; |
33 | use MediaWiki\MainConfigNames; |
34 | use MediaWiki\MediaWikiServices; |
35 | use MediaWiki\Page\PageReference; |
36 | use MediaWiki\Title\Title; |
37 | use MediaWiki\User\ActorNormalization; |
38 | use Wikimedia\Rdbms\IExpression; |
39 | use Wikimedia\Rdbms\LikeValue; |
40 | |
41 | /** |
42 | * @ingroup Pager |
43 | */ |
44 | class LogPager extends ReverseChronologicalPager { |
45 | /** @var array Log types */ |
46 | private $types = []; |
47 | |
48 | /** @var string Events limited to those by performer when set */ |
49 | private $performer = ''; |
50 | |
51 | /** @var string Events limited to those about this page when set */ |
52 | private $page = ''; |
53 | |
54 | /** @var bool */ |
55 | private $pattern = false; |
56 | |
57 | /** @var string */ |
58 | private $typeCGI = ''; |
59 | |
60 | /** @var string */ |
61 | private $action = ''; |
62 | |
63 | /** @var bool */ |
64 | private $performerRestrictionsEnforced = false; |
65 | |
66 | /** @var bool */ |
67 | private $actionRestrictionsEnforced = false; |
68 | |
69 | /** @var array */ |
70 | private $mConds; |
71 | |
72 | /** @var string */ |
73 | private $mTagFilter; |
74 | |
75 | /** @var bool */ |
76 | private $mTagInvert; |
77 | |
78 | /** @var LogEventsList */ |
79 | public $mLogEventsList; |
80 | |
81 | /** @var LinkBatchFactory */ |
82 | private $linkBatchFactory; |
83 | |
84 | /** @var ActorNormalization */ |
85 | private $actorNormalization; |
86 | private LogFormatterFactory $logFormatterFactory; |
87 | |
88 | /** |
89 | * @param LogEventsList $list |
90 | * @param string|array $types Log types to show |
91 | * @param string $performer The user who made the log entries |
92 | * @param string|PageReference $page The page the log entries are for |
93 | * @param bool $pattern Do a prefix search rather than an exact title match |
94 | * @param array $conds Extra conditions for the query |
95 | * @param int|bool $year The year to start from. Default: false |
96 | * @param int|bool $month The month to start from. Default: false |
97 | * @param int|bool $day The day to start from. Default: false |
98 | * @param string $tagFilter Tag |
99 | * @param string $action Specific action (subtype) requested |
100 | * @param int $logId Log entry ID, to limit to a single log entry. |
101 | * @param LinkBatchFactory|null $linkBatchFactory |
102 | * @param ActorNormalization|null $actorNormalization |
103 | * @param LogFormatterFactory|null $logFormatterFactory |
104 | * @param bool $tagInvert whether tags are filtered for (false) or out (true) |
105 | */ |
106 | public function __construct( $list, $types = [], $performer = '', $page = '', |
107 | $pattern = false, $conds = [], $year = false, $month = false, $day = false, |
108 | $tagFilter = '', $action = '', $logId = 0, |
109 | ?LinkBatchFactory $linkBatchFactory = null, |
110 | ?ActorNormalization $actorNormalization = null, |
111 | ?LogFormatterFactory $logFormatterFactory = null, |
112 | $tagInvert = false |
113 | ) { |
114 | parent::__construct( $list->getContext() ); |
115 | |
116 | $services = MediaWikiServices::getInstance(); |
117 | $this->mConds = $conds; |
118 | $this->mLogEventsList = $list; |
119 | |
120 | // Class is used directly in extensions - T266480 |
121 | $this->linkBatchFactory = $linkBatchFactory ?? $services->getLinkBatchFactory(); |
122 | $this->actorNormalization = $actorNormalization ?? $services->getActorNormalization(); |
123 | $this->logFormatterFactory = $logFormatterFactory ?? $services->getLogFormatterFactory(); |
124 | |
125 | $this->limitLogId( $logId ); // set before types per T269761 |
126 | $this->limitType( $types ); // also excludes hidden types |
127 | $this->limitFilterTypes(); |
128 | $this->limitPerformer( $performer ); |
129 | $this->limitTitle( $page, $pattern ); |
130 | $this->limitAction( $action ); |
131 | $this->getDateCond( $year, $month, $day ); |
132 | $this->mTagFilter = (string)$tagFilter; |
133 | $this->mTagInvert = (bool)$tagInvert; |
134 | } |
135 | |
136 | public function getDefaultQuery() { |
137 | $query = parent::getDefaultQuery(); |
138 | $query['type'] = $this->typeCGI; // arrays won't work here |
139 | $query['user'] = $this->performer; |
140 | $query['day'] = $this->mDay; |
141 | $query['month'] = $this->mMonth; |
142 | $query['year'] = $this->mYear; |
143 | |
144 | return $query; |
145 | } |
146 | |
147 | private function limitFilterTypes() { |
148 | if ( $this->hasEqualsClause( 'log_id' ) ) { // T220834 |
149 | return; |
150 | } |
151 | $filterTypes = $this->getFilterParams(); |
152 | $excludeTypes = []; |
153 | foreach ( $filterTypes as $type => $hide ) { |
154 | if ( $hide ) { |
155 | $excludeTypes[] = $type; |
156 | } |
157 | } |
158 | if ( $excludeTypes ) { |
159 | $this->mConds[] = $this->mDb->expr( 'log_type', '!=', $excludeTypes ); |
160 | } |
161 | } |
162 | |
163 | public function getFilterParams() { |
164 | $filters = []; |
165 | if ( count( $this->types ) ) { |
166 | return $filters; |
167 | } |
168 | |
169 | // FIXME: This is broken, values from HTMLForm should be used. |
170 | $wpfilters = $this->getRequest()->getArray( "wpfilters" ); |
171 | $filterLogTypes = $this->getConfig()->get( MainConfigNames::FilterLogTypes ); |
172 | |
173 | foreach ( $filterLogTypes as $type => $default ) { |
174 | // Back-compat: Check old URL params if the new param wasn't passed |
175 | if ( $wpfilters === null ) { |
176 | $hide = $this->getRequest()->getBool( "hide_{$type}_log", $default ); |
177 | } else { |
178 | $hide = !in_array( $type, $wpfilters ); |
179 | } |
180 | |
181 | $filters[$type] = $hide; |
182 | } |
183 | |
184 | return $filters; |
185 | } |
186 | |
187 | /** |
188 | * Set the log reader to return only entries of the given type. |
189 | * Type restrictions enforced here |
190 | * |
191 | * @param string|array $types Log types ('upload', 'delete', etc); |
192 | * empty string means no restriction |
193 | */ |
194 | private function limitType( $types ) { |
195 | $restrictions = $this->getConfig()->get( MainConfigNames::LogRestrictions ); |
196 | // If $types is not an array, make it an array |
197 | $types = ( $types === '' ) ? [] : (array)$types; |
198 | // Don't even show header for private logs; don't recognize it... |
199 | $needReindex = false; |
200 | foreach ( $types as $type ) { |
201 | if ( isset( $restrictions[$type] ) |
202 | && !$this->getAuthority()->isAllowed( $restrictions[$type] ) |
203 | ) { |
204 | $needReindex = true; |
205 | $types = array_diff( $types, [ $type ] ); |
206 | } |
207 | } |
208 | if ( $needReindex ) { |
209 | // Lots of this code makes assumptions that |
210 | // the first entry in the array is $types[0]. |
211 | $types = array_values( $types ); |
212 | } |
213 | $this->types = $types; |
214 | // Don't show private logs to unprivileged users. |
215 | // Also, only show them upon specific request to avoid surprises. |
216 | // Exception: if we are showing only a single log entry based on the log id, |
217 | // we don't require that "specific request" so that the links-in-logs feature |
218 | // works. See T269761 |
219 | $audience = ( $types || $this->hasEqualsClause( 'log_id' ) ) ? 'user' : 'public'; |
220 | $hideLogs = LogEventsList::getExcludeClause( $this->mDb, $audience, $this->getAuthority() ); |
221 | if ( $hideLogs !== false ) { |
222 | $this->mConds[] = $hideLogs; |
223 | } |
224 | if ( count( $types ) ) { |
225 | $this->mConds['log_type'] = $types; |
226 | // Set typeCGI; used in url param for paging |
227 | if ( count( $types ) == 1 ) { |
228 | $this->typeCGI = $types[0]; |
229 | } |
230 | } |
231 | } |
232 | |
233 | /** |
234 | * Set the log reader to return only entries by the given user. |
235 | * |
236 | * @param string $name (In)valid user name |
237 | * @return void |
238 | */ |
239 | private function limitPerformer( $name ) { |
240 | if ( $name == '' ) { |
241 | return; |
242 | } |
243 | |
244 | $actorId = $this->actorNormalization->findActorIdByName( $name, $this->mDb ); |
245 | |
246 | if ( !$actorId ) { |
247 | // Unknown user, match nothing. |
248 | $this->mConds[] = '1 = 0'; |
249 | return; |
250 | } |
251 | |
252 | $this->mConds[ 'log_actor' ] = $actorId; |
253 | |
254 | $this->enforcePerformerRestrictions(); |
255 | |
256 | $this->performer = $name; |
257 | } |
258 | |
259 | /** |
260 | * Set the log reader to return only entries affecting the given page. |
261 | * (For the block and rights logs, this is a user page.) |
262 | * |
263 | * @param string|PageReference $page |
264 | * @param bool $pattern |
265 | * @return void |
266 | */ |
267 | private function limitTitle( $page, $pattern ) { |
268 | if ( !$page instanceof PageReference ) { |
269 | // NOTE: For some types of logs, the title may be something strange, like "User:#12345"! |
270 | $page = Title::newFromText( $page ); |
271 | if ( !$page ) { |
272 | return; |
273 | } |
274 | } |
275 | |
276 | $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter(); |
277 | $this->page = $titleFormatter->getPrefixedDBkey( $page ); |
278 | $ns = $page->getNamespace(); |
279 | $db = $this->mDb; |
280 | |
281 | $interwikiDelimiter = $this->getConfig()->get( MainConfigNames::UserrightsInterwikiDelimiter ); |
282 | |
283 | $doUserRightsLogLike = false; |
284 | if ( $this->types == [ 'rights' ] ) { |
285 | $parts = explode( $interwikiDelimiter, $page->getDBkey() ); |
286 | if ( count( $parts ) == 2 ) { |
287 | [ $name, $database ] = array_map( 'trim', $parts ); |
288 | if ( str_contains( $database, '*' ) ) { |
289 | $doUserRightsLogLike = true; |
290 | } |
291 | } |
292 | } |
293 | |
294 | /** |
295 | * Using the (log_namespace, log_title, log_timestamp) index with a |
296 | * range scan (LIKE) on the first two parts, instead of simple equality, |
297 | * makes it unusable for sorting. Sorted retrieval using another index |
298 | * would be possible, but then we might have to scan arbitrarily many |
299 | * nodes of that index. Therefore, we need to avoid this if $wgMiserMode |
300 | * is on. |
301 | * |
302 | * This is not a problem with simple title matches, because then we can |
303 | * use the log_page_time index. That should have no more than a few hundred |
304 | * log entries for even the busiest pages, so it can be safely scanned |
305 | * in full to satisfy an impossible condition on user or similar. |
306 | */ |
307 | $this->mConds['log_namespace'] = $ns; |
308 | if ( $doUserRightsLogLike ) { |
309 | // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $name is set when reached here |
310 | $params = [ $name . $interwikiDelimiter ]; |
311 | // @phan-suppress-next-next-line PhanPossiblyUndeclaredVariable $database is set when reached here |
312 | // @phan-suppress-next-line PhanTypeMismatchArgumentNullableInternal $database is set when reached here |
313 | $databaseParts = explode( '*', $database ); |
314 | $databasePartCount = count( $databaseParts ); |
315 | foreach ( $databaseParts as $i => $databasepart ) { |
316 | $params[] = $databasepart; |
317 | if ( $i < $databasePartCount - 1 ) { |
318 | $params[] = $db->anyString(); |
319 | } |
320 | } |
321 | $this->mConds[] = $db->expr( 'log_title', IExpression::LIKE, new LikeValue( ...$params ) ); |
322 | } elseif ( $pattern && !$this->getConfig()->get( MainConfigNames::MiserMode ) ) { |
323 | $this->mConds[] = $db->expr( |
324 | 'log_title', |
325 | IExpression::LIKE, |
326 | new LikeValue( $page->getDBkey(), $db->anyString() ) |
327 | ); |
328 | $this->pattern = $pattern; |
329 | } else { |
330 | $this->mConds['log_title'] = $page->getDBkey(); |
331 | } |
332 | $this->enforceActionRestrictions(); |
333 | } |
334 | |
335 | /** |
336 | * Set the log_action field to a specified value (or values) |
337 | * |
338 | * @param string $action |
339 | */ |
340 | private function limitAction( $action ) { |
341 | // Allow to filter the log by actions |
342 | $type = $this->typeCGI; |
343 | if ( $type === '' ) { |
344 | // nothing to do |
345 | return; |
346 | } |
347 | $actions = $this->getConfig()->get( MainConfigNames::ActionFilteredLogs ); |
348 | if ( isset( $actions[$type] ) ) { |
349 | // log type can be filtered by actions |
350 | if ( $action !== '' && isset( $actions[$type][$action] ) ) { |
351 | // add condition to query |
352 | $this->mConds['log_action'] = $actions[$type][$action]; |
353 | $this->action = $action; |
354 | } |
355 | } |
356 | } |
357 | |
358 | /** |
359 | * Limit to the (single) specified log ID. |
360 | * @param int $logId The log entry ID. |
361 | */ |
362 | protected function limitLogId( $logId ) { |
363 | if ( !$logId ) { |
364 | return; |
365 | } |
366 | $this->mConds['log_id'] = $logId; |
367 | } |
368 | |
369 | /** |
370 | * Constructs the most part of the query. Extra conditions are sprinkled in |
371 | * all over this class. |
372 | * @return array |
373 | */ |
374 | public function getQueryInfo() { |
375 | $queryBuilder = DatabaseLogEntry::newSelectQueryBuilder( $this->mDb ) |
376 | ->where( $this->mConds ); |
377 | |
378 | # Add log_search table if there are conditions on it. |
379 | # This filters the results to only include log rows that have |
380 | # log_search records with the specified ls_field and ls_value values. |
381 | if ( array_key_exists( 'ls_field', $this->mConds ) ) { |
382 | $queryBuilder->join( 'log_search', null, 'ls_log_id=log_id' ); |
383 | $queryBuilder->ignoreIndex( [ 'log_search' => 'ls_log_id' ] ); |
384 | $queryBuilder->useIndex( [ 'logging' => 'PRIMARY' ] ); |
385 | if ( !$this->hasEqualsClause( 'ls_field' ) |
386 | || !$this->hasEqualsClause( 'ls_value' ) |
387 | ) { |
388 | # Since (ls_field,ls_value,ls_logid) is unique, if the condition is |
389 | # to match a specific (ls_field,ls_value) tuple, then there will be |
390 | # no duplicate log rows. Otherwise, we need to remove the duplicates. |
391 | $queryBuilder->distinct(); |
392 | } |
393 | } elseif ( array_key_exists( 'log_actor', $this->mConds ) ) { |
394 | // Optimizer doesn't pick the right index when a user has lots of log actions (T303089) |
395 | $index = 'log_actor_time'; |
396 | foreach ( $this->getFilterParams() as $hide ) { |
397 | if ( !$hide ) { |
398 | $index = 'log_actor_type_time'; |
399 | break; |
400 | } |
401 | } |
402 | $queryBuilder->useIndex( [ 'logging' => $index ] ); |
403 | } |
404 | |
405 | // T221458: MySQL/MariaDB (10.1.37) can sometimes irrationally decide that querying `actor` before |
406 | // `logging` and filesorting is somehow better than querying $limit+1 rows from `logging`. |
407 | // Tell it not to reorder the query. But not when tag filtering or log_search was used, as it |
408 | // seems as likely to be harmed as helped in that case. |
409 | if ( $this->mTagFilter === '' && !array_key_exists( 'ls_field', $this->mConds ) ) { |
410 | $queryBuilder->straightJoinOption(); |
411 | } |
412 | |
413 | $maxExecTime = $this->getConfig()->get( MainConfigNames::MaxExecutionTimeForExpensiveQueries ); |
414 | if ( $maxExecTime ) { |
415 | $queryBuilder->setMaxExecutionTime( $maxExecTime ); |
416 | } |
417 | |
418 | # Add ChangeTags filter query |
419 | MediaWikiServices::getInstance()->getChangeTagsStore()->modifyDisplayQueryBuilder( |
420 | $queryBuilder, |
421 | 'logging', |
422 | $this->mTagFilter, |
423 | $this->mTagInvert |
424 | ); |
425 | |
426 | return $queryBuilder->getQueryInfo(); |
427 | } |
428 | |
429 | /** |
430 | * Checks if $this->mConds has $field matched to a *single* value |
431 | * @param string $field |
432 | * @return bool |
433 | */ |
434 | protected function hasEqualsClause( $field ) { |
435 | return ( |
436 | array_key_exists( $field, $this->mConds ) && |
437 | ( !is_array( $this->mConds[$field] ) || count( $this->mConds[$field] ) == 1 ) |
438 | ); |
439 | } |
440 | |
441 | public function getIndexField() { |
442 | return [ [ 'log_timestamp', 'log_id' ] ]; |
443 | } |
444 | |
445 | protected function doBatchLookups() { |
446 | $lb = $this->linkBatchFactory->newLinkBatch(); |
447 | foreach ( $this->mResult as $row ) { |
448 | $lb->add( $row->log_namespace, $row->log_title ); |
449 | $lb->add( NS_USER, $row->log_user_text ); |
450 | $lb->add( NS_USER_TALK, $row->log_user_text ); |
451 | $formatter = $this->logFormatterFactory->newFromRow( $row ); |
452 | foreach ( $formatter->getPreloadTitles() as $title ) { |
453 | $lb->addObj( $title ); |
454 | } |
455 | } |
456 | $lb->execute(); |
457 | } |
458 | |
459 | public function formatRow( $row ) { |
460 | return $this->mLogEventsList->logLine( $row ); |
461 | } |
462 | |
463 | public function getType() { |
464 | return $this->types; |
465 | } |
466 | |
467 | /** |
468 | * Guaranteed to either return a valid title string or a Zero-Length String |
469 | * |
470 | * @return string |
471 | */ |
472 | public function getPerformer() { |
473 | return $this->performer; |
474 | } |
475 | |
476 | /** |
477 | * @return string |
478 | */ |
479 | public function getPage() { |
480 | return $this->page; |
481 | } |
482 | |
483 | /** |
484 | * @return bool |
485 | */ |
486 | public function getPattern() { |
487 | return $this->pattern; |
488 | } |
489 | |
490 | public function getYear() { |
491 | return $this->mYear; |
492 | } |
493 | |
494 | public function getMonth() { |
495 | return $this->mMonth; |
496 | } |
497 | |
498 | public function getDay() { |
499 | return $this->mDay; |
500 | } |
501 | |
502 | public function getTagFilter() { |
503 | return $this->mTagFilter; |
504 | } |
505 | |
506 | public function getTagInvert() { |
507 | return $this->mTagInvert; |
508 | } |
509 | |
510 | public function getAction() { |
511 | return $this->action; |
512 | } |
513 | |
514 | /** |
515 | * Paranoia: avoid brute force searches (T19342) |
516 | */ |
517 | private function enforceActionRestrictions() { |
518 | if ( $this->actionRestrictionsEnforced ) { |
519 | return; |
520 | } |
521 | $this->actionRestrictionsEnforced = true; |
522 | if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) { |
523 | $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::DELETED_ACTION ) . ' = 0'; |
524 | } elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) { |
525 | $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::SUPPRESSED_ACTION ) . |
526 | ' != ' . LogPage::SUPPRESSED_USER; |
527 | } |
528 | } |
529 | |
530 | /** |
531 | * Paranoia: avoid brute force searches (T19342) |
532 | */ |
533 | private function enforcePerformerRestrictions() { |
534 | // Same as enforceActionRestrictions(), except for _USER instead of _ACTION bits. |
535 | if ( $this->performerRestrictionsEnforced ) { |
536 | return; |
537 | } |
538 | $this->performerRestrictionsEnforced = true; |
539 | if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) { |
540 | $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::DELETED_USER ) . ' = 0'; |
541 | } elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) { |
542 | $this->mConds[] = $this->mDb->bitAnd( 'log_deleted', LogPage::SUPPRESSED_USER ) . |
543 | ' != ' . LogPage::SUPPRESSED_ACTION; |
544 | } |
545 | } |
546 | } |
547 | |
548 | /** @deprecated class alias since 1.41 */ |
549 | class_alias( LogPager::class, 'LogPager' ); |