Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
44.67% |
155 / 347 |
|
0.00% |
0 / 11 |
CRAP | |
0.00% |
0 / 1 |
BlockListPager | |
44.80% |
155 / 346 |
|
0.00% |
0 / 11 |
869.88 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
12 | |||
getFieldNames | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
12 | |||
formatValue | |
72.81% |
83 / 114 |
|
0.00% |
0 / 1 |
35.58 | |||
formatTarget | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
56 | |||
getRestrictionListHTML | |
78.69% |
48 / 61 |
|
0.00% |
0 / 1 |
14.64 | |||
getQueryInfo | |
0.00% |
0 / 83 |
|
0.00% |
0 / 1 |
20 | |||
getTableClass | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getIndexField | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getDefaultSort | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isFieldSortable | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
preprocessResults | |
96.00% |
24 / 25 |
|
0.00% |
0 / 1 |
10 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | * @ingroup Pager |
20 | */ |
21 | |
22 | namespace MediaWiki\Pager; |
23 | |
24 | use MediaWiki\Block\Block; |
25 | use MediaWiki\Block\BlockActionInfo; |
26 | use MediaWiki\Block\BlockRestrictionStore; |
27 | use MediaWiki\Block\BlockUtils; |
28 | use MediaWiki\Block\HideUserUtils; |
29 | use MediaWiki\Block\Restriction\ActionRestriction; |
30 | use MediaWiki\Block\Restriction\NamespaceRestriction; |
31 | use MediaWiki\Block\Restriction\PageRestriction; |
32 | use MediaWiki\Block\Restriction\Restriction; |
33 | use MediaWiki\Cache\LinkBatchFactory; |
34 | use MediaWiki\CommentFormatter\RowCommentFormatter; |
35 | use MediaWiki\CommentStore\CommentStore; |
36 | use MediaWiki\Config\ConfigException; |
37 | use MediaWiki\Context\IContextSource; |
38 | use MediaWiki\Html\Html; |
39 | use MediaWiki\Linker\Linker; |
40 | use MediaWiki\Linker\LinkRenderer; |
41 | use MediaWiki\MainConfigNames; |
42 | use MediaWiki\SpecialPage\SpecialPageFactory; |
43 | use MediaWiki\User\UserIdentity; |
44 | use MediaWiki\Utils\MWTimestamp; |
45 | use stdClass; |
46 | use Wikimedia\Rdbms\IConnectionProvider; |
47 | use Wikimedia\Rdbms\IResultWrapper; |
48 | |
49 | /** |
50 | * @ingroup Pager |
51 | */ |
52 | class BlockListPager extends TablePager { |
53 | |
54 | protected $conds; |
55 | |
56 | /** |
57 | * Array of restrictions. |
58 | * |
59 | * @var Restriction[] |
60 | */ |
61 | protected $restrictions = []; |
62 | |
63 | private BlockActionInfo $blockActionInfo; |
64 | private BlockRestrictionStore $blockRestrictionStore; |
65 | private BlockUtils $blockUtils; |
66 | private HideUserUtils $hideUserUtils; |
67 | private CommentStore $commentStore; |
68 | private LinkBatchFactory $linkBatchFactory; |
69 | private RowCommentFormatter $rowCommentFormatter; |
70 | private SpecialPageFactory $specialPageFactory; |
71 | |
72 | /** @var string[] */ |
73 | private $formattedComments = []; |
74 | |
75 | /** @var int */ |
76 | private $readStage; |
77 | |
78 | /** |
79 | * @param IContextSource $context |
80 | * @param BlockActionInfo $blockActionInfo |
81 | * @param BlockRestrictionStore $blockRestrictionStore |
82 | * @param BlockUtils $blockUtils |
83 | * @param HideUserUtils $hideUserUtils |
84 | * @param CommentStore $commentStore |
85 | * @param LinkBatchFactory $linkBatchFactory |
86 | * @param LinkRenderer $linkRenderer |
87 | * @param IConnectionProvider $dbProvider |
88 | * @param RowCommentFormatter $rowCommentFormatter |
89 | * @param SpecialPageFactory $specialPageFactory |
90 | * @param array $conds |
91 | */ |
92 | public function __construct( |
93 | IContextSource $context, |
94 | BlockActionInfo $blockActionInfo, |
95 | BlockRestrictionStore $blockRestrictionStore, |
96 | BlockUtils $blockUtils, |
97 | HideUserUtils $hideUserUtils, |
98 | CommentStore $commentStore, |
99 | LinkBatchFactory $linkBatchFactory, |
100 | LinkRenderer $linkRenderer, |
101 | IConnectionProvider $dbProvider, |
102 | RowCommentFormatter $rowCommentFormatter, |
103 | SpecialPageFactory $specialPageFactory, |
104 | $conds |
105 | ) { |
106 | // Set database before parent constructor to avoid setting it there |
107 | $this->mDb = $dbProvider->getReplicaDatabase(); |
108 | $this->readStage = $this->getConfig()->get( MainConfigNames::BlockTargetMigrationStage ) |
109 | & SCHEMA_COMPAT_READ_MASK; |
110 | if ( $this->readStage !== SCHEMA_COMPAT_READ_OLD |
111 | && $this->readStage !== SCHEMA_COMPAT_READ_NEW |
112 | ) { |
113 | throw new ConfigException( |
114 | '$wgBlockTargetMigrationStage has an invalid read stage' ); |
115 | } |
116 | |
117 | parent::__construct( $context, $linkRenderer ); |
118 | |
119 | $this->blockActionInfo = $blockActionInfo; |
120 | $this->blockRestrictionStore = $blockRestrictionStore; |
121 | $this->blockUtils = $blockUtils; |
122 | $this->hideUserUtils = $hideUserUtils; |
123 | $this->commentStore = $commentStore; |
124 | $this->linkBatchFactory = $linkBatchFactory; |
125 | $this->rowCommentFormatter = $rowCommentFormatter; |
126 | $this->specialPageFactory = $specialPageFactory; |
127 | $this->conds = $conds; |
128 | $this->mDefaultDirection = IndexPager::DIR_DESCENDING; |
129 | } |
130 | |
131 | protected function getFieldNames() { |
132 | static $headers = null; |
133 | |
134 | if ( $headers === null ) { |
135 | $headers = [ |
136 | 'bl_timestamp' => 'blocklist-timestamp', |
137 | 'target' => 'blocklist-target', |
138 | 'bl_expiry' => 'blocklist-expiry', |
139 | 'by' => 'blocklist-by', |
140 | 'params' => 'blocklist-params', |
141 | 'bl_reason' => 'blocklist-reason', |
142 | ]; |
143 | foreach ( $headers as $key => $val ) { |
144 | $headers[$key] = $this->msg( $val )->text(); |
145 | } |
146 | } |
147 | |
148 | return $headers; |
149 | } |
150 | |
151 | /** |
152 | * @param string $name |
153 | * @param string|null $value |
154 | * @return string |
155 | * @suppress PhanTypeArraySuspicious |
156 | */ |
157 | public function formatValue( $name, $value ) { |
158 | static $msg = null; |
159 | if ( $msg === null ) { |
160 | $keys = [ |
161 | 'anononlyblock', |
162 | 'createaccountblock', |
163 | 'noautoblockblock', |
164 | 'emailblock', |
165 | 'blocklist-nousertalk', |
166 | 'unblocklink', |
167 | 'change-blocklink', |
168 | 'blocklist-editing', |
169 | 'blocklist-editing-sitewide', |
170 | 'blocklist-hidden-param', |
171 | ]; |
172 | |
173 | foreach ( $keys as $key ) { |
174 | $msg[$key] = $this->msg( $key )->text(); |
175 | } |
176 | } |
177 | '@phan-var string[] $msg'; |
178 | |
179 | /** @var stdClass $row */ |
180 | $row = $this->mCurrentRow; |
181 | |
182 | $language = $this->getLanguage(); |
183 | |
184 | $formatted = ''; |
185 | |
186 | $linkRenderer = $this->getLinkRenderer(); |
187 | |
188 | switch ( $name ) { |
189 | case 'bl_timestamp': |
190 | $formatted = htmlspecialchars( $language->userTimeAndDate( $value, $this->getUser() ) ); |
191 | break; |
192 | |
193 | case 'target': |
194 | $formatted = $this->formatTarget( $row ); |
195 | break; |
196 | |
197 | case 'bl_expiry': |
198 | $formatted = htmlspecialchars( $language->formatExpiry( |
199 | $value, |
200 | /* User preference timezone */true, |
201 | 'infinity', |
202 | $this->getUser() |
203 | ) ); |
204 | if ( $this->getAuthority()->isAllowed( 'block' ) ) { |
205 | $links = []; |
206 | if ( $row->bt_auto ) { |
207 | $links[] = $linkRenderer->makeKnownLink( |
208 | $this->specialPageFactory->getTitleForAlias( 'Unblock' ), |
209 | $msg['unblocklink'], |
210 | [], |
211 | [ 'wpTarget' => "#{$row->bl_id}" ] |
212 | ); |
213 | } else { |
214 | $target = $row->bt_address ?? $row->bt_user_text; |
215 | $links[] = $linkRenderer->makeKnownLink( |
216 | $this->specialPageFactory->getTitleForAlias( "Unblock/$target" ), |
217 | $msg['unblocklink'] |
218 | ); |
219 | $links[] = $linkRenderer->makeKnownLink( |
220 | $this->specialPageFactory->getTitleForAlias( "Block/$target" ), |
221 | $msg['change-blocklink'] |
222 | ); |
223 | } |
224 | $formatted .= ' ' . Html::rawElement( |
225 | 'span', |
226 | [ 'class' => 'mw-blocklist-actions' ], |
227 | $this->msg( 'parentheses' )->rawParams( |
228 | $language->pipeList( $links ) )->escaped() |
229 | ); |
230 | } |
231 | if ( $value !== 'infinity' ) { |
232 | $timestamp = new MWTimestamp( $value ); |
233 | $formatted .= '<br />' . $this->msg( |
234 | 'ipb-blocklist-duration-left', |
235 | $language->formatDuration( |
236 | (int)$timestamp->getTimestamp( TS_UNIX ) - MWTimestamp::time(), |
237 | // reasonable output |
238 | [ |
239 | 'minutes', |
240 | 'hours', |
241 | 'days', |
242 | 'years', |
243 | ] |
244 | ) |
245 | )->escaped(); |
246 | } |
247 | break; |
248 | |
249 | case 'by': |
250 | $formatted = Linker::userLink( (int)$value, $row->bl_by_text ); |
251 | $formatted .= Linker::userToolLinks( (int)$value, $row->bl_by_text ); |
252 | break; |
253 | |
254 | case 'bl_reason': |
255 | $formatted = $this->formattedComments[$this->getResultOffset()]; |
256 | break; |
257 | |
258 | case 'params': |
259 | $properties = []; |
260 | |
261 | if ( $row->bl_deleted ) { |
262 | $properties[] = htmlspecialchars( $msg['blocklist-hidden-param' ] ); |
263 | } |
264 | if ( $row->bl_sitewide ) { |
265 | $properties[] = htmlspecialchars( $msg['blocklist-editing-sitewide'] ); |
266 | } |
267 | |
268 | if ( !$row->bl_sitewide && $this->restrictions ) { |
269 | $list = $this->getRestrictionListHTML( $row ); |
270 | if ( $list ) { |
271 | $properties[] = htmlspecialchars( $msg['blocklist-editing'] ) . $list; |
272 | } |
273 | } |
274 | |
275 | if ( $row->bl_anon_only ) { |
276 | $properties[] = htmlspecialchars( $msg['anononlyblock'] ); |
277 | } |
278 | if ( $row->bl_create_account ) { |
279 | $properties[] = htmlspecialchars( $msg['createaccountblock'] ); |
280 | } |
281 | if ( $row->bt_user && !$row->bl_enable_autoblock ) { |
282 | $properties[] = htmlspecialchars( $msg['noautoblockblock'] ); |
283 | } |
284 | |
285 | if ( $row->bl_block_email ) { |
286 | $properties[] = htmlspecialchars( $msg['emailblock'] ); |
287 | } |
288 | |
289 | if ( !$row->bl_allow_usertalk ) { |
290 | $properties[] = htmlspecialchars( $msg['blocklist-nousertalk'] ); |
291 | } |
292 | |
293 | $formatted = Html::rawElement( |
294 | 'ul', |
295 | [], |
296 | implode( '', array_map( static function ( $prop ) { |
297 | return Html::rawElement( |
298 | 'li', |
299 | [], |
300 | $prop |
301 | ); |
302 | }, $properties ) ) |
303 | ); |
304 | break; |
305 | |
306 | default: |
307 | $formatted = "Unable to format $name"; |
308 | break; |
309 | } |
310 | |
311 | return $formatted; |
312 | } |
313 | |
314 | /** |
315 | * Format the target field |
316 | * @param stdClass $row |
317 | * @return string |
318 | */ |
319 | private function formatTarget( $row ) { |
320 | if ( $row->bt_auto ) { |
321 | return $this->msg( 'autoblockid', $row->bl_id )->parse(); |
322 | } |
323 | |
324 | [ $target, $type ] = $this->blockUtils->parseBlockTargetRow( $row ); |
325 | |
326 | if ( $type === Block::TYPE_RANGE ) { |
327 | $userId = 0; |
328 | $userName = $target; |
329 | } elseif ( ( $row->hu_deleted ?? null ) |
330 | && !$this->getAuthority()->isAllowed( 'hideuser' ) |
331 | ) { |
332 | return Html::element( |
333 | 'span', |
334 | [ 'class' => 'mw-blocklist-hidden' ], |
335 | $this->msg( 'blocklist-hidden-placeholder' )->text() |
336 | ); |
337 | } elseif ( $target instanceof UserIdentity ) { |
338 | $userId = $target->getId(); |
339 | $userName = $target->getName(); |
340 | } elseif ( is_string( $target ) ) { |
341 | return htmlspecialchars( $target ); |
342 | } else { |
343 | return $this->msg( 'empty-username' )->escaped(); |
344 | } |
345 | return Linker::userLink( $userId, $userName ) . |
346 | Linker::userToolLinks( |
347 | $userId, |
348 | $userName, |
349 | false, |
350 | Linker::TOOL_LINKS_NOBLOCK |
351 | ); |
352 | } |
353 | |
354 | /** |
355 | * Get Restriction List HTML |
356 | * |
357 | * @param stdClass $row |
358 | * |
359 | * @return string |
360 | */ |
361 | private function getRestrictionListHTML( stdClass $row ) { |
362 | $items = []; |
363 | $linkRenderer = $this->getLinkRenderer(); |
364 | |
365 | foreach ( $this->restrictions as $restriction ) { |
366 | if ( $restriction->getBlockId() !== (int)$row->bl_id ) { |
367 | continue; |
368 | } |
369 | |
370 | switch ( $restriction->getType() ) { |
371 | case PageRestriction::TYPE: |
372 | '@phan-var PageRestriction $restriction'; |
373 | if ( $restriction->getTitle() ) { |
374 | $items[$restriction->getType()][] = Html::rawElement( |
375 | 'li', |
376 | [], |
377 | $linkRenderer->makeLink( $restriction->getTitle() ) |
378 | ); |
379 | } |
380 | break; |
381 | case NamespaceRestriction::TYPE: |
382 | $text = $restriction->getValue() === NS_MAIN |
383 | ? $this->msg( 'blanknamespace' )->text() |
384 | : $this->getLanguage()->getFormattedNsText( |
385 | $restriction->getValue() |
386 | ); |
387 | if ( $text ) { |
388 | $items[$restriction->getType()][] = Html::rawElement( |
389 | 'li', |
390 | [], |
391 | $linkRenderer->makeLink( |
392 | $this->specialPageFactory->getTitleForAlias( 'Allpages' ), |
393 | $text, |
394 | [], |
395 | [ |
396 | 'namespace' => $restriction->getValue() |
397 | ] |
398 | ) |
399 | ); |
400 | } |
401 | break; |
402 | case ActionRestriction::TYPE: |
403 | $actionName = $this->blockActionInfo->getActionFromId( $restriction->getValue() ); |
404 | $enablePartialActionBlocks = |
405 | $this->getConfig()->get( MainConfigNames::EnablePartialActionBlocks ); |
406 | if ( $actionName && $enablePartialActionBlocks ) { |
407 | $items[$restriction->getType()][] = Html::rawElement( |
408 | 'li', |
409 | [], |
410 | $this->msg( 'ipb-action-' . |
411 | $this->blockActionInfo->getActionFromId( $restriction->getValue() ) )->escaped() |
412 | ); |
413 | } |
414 | break; |
415 | } |
416 | } |
417 | |
418 | if ( !$items ) { |
419 | return ''; |
420 | } |
421 | |
422 | $sets = []; |
423 | foreach ( $items as $key => $value ) { |
424 | $sets[] = Html::rawElement( |
425 | 'li', |
426 | [], |
427 | $this->msg( 'blocklist-editing-' . $key ) . Html::rawElement( |
428 | 'ul', |
429 | [], |
430 | implode( '', $value ) |
431 | ) |
432 | ); |
433 | } |
434 | |
435 | return Html::rawElement( |
436 | 'ul', |
437 | [], |
438 | implode( '', $sets ) |
439 | ); |
440 | } |
441 | |
442 | public function getQueryInfo() { |
443 | $db = $this->getDatabase(); |
444 | if ( $this->readStage === SCHEMA_COMPAT_READ_OLD ) { |
445 | $commentQuery = $this->commentStore->getJoin( 'ipb_reason' ); |
446 | $info = [ |
447 | 'tables' => array_merge( |
448 | [ 'ipblocks', 'ipblocks_by_actor' => 'actor' ], |
449 | $commentQuery['tables'] |
450 | ), |
451 | 'fields' => [ |
452 | 'bt_address' => 'ipb_address', |
453 | 'bt_user_text' => 'ipb_address', |
454 | 'bt_user' => 'ipb_user', |
455 | 'bt_auto' => 'ipb_auto', |
456 | 'bt_range_start' => 'ipb_range_start', |
457 | 'bt_range_end' => 'ipb_range_end', |
458 | 'bl_id' => 'ipb_id', |
459 | 'bl_by' => 'ipblocks_by_actor.actor_user', |
460 | 'bl_by_text' => 'ipblocks_by_actor.actor_name', |
461 | 'bl_timestamp' => 'ipb_timestamp', |
462 | 'bl_anon_only' => 'ipb_anon_only', |
463 | 'bl_create_account' => 'ipb_create_account', |
464 | 'bl_enable_autoblock' => 'ipb_enable_autoblock', |
465 | 'bl_expiry' => 'ipb_expiry', |
466 | 'bl_deleted' => 'ipb_deleted', |
467 | 'bl_block_email' => 'ipb_block_email', |
468 | 'bl_allow_usertalk' => 'ipb_allow_usertalk', |
469 | 'bl_sitewide' => 'ipb_sitewide', |
470 | 'bl_reason_text' => $commentQuery['fields']['ipb_reason_text'], |
471 | 'bl_reason_data' => $commentQuery['fields']['ipb_reason_data'], |
472 | 'bl_reason_cid' => $commentQuery['fields']['ipb_reason_cid'], |
473 | // Aliases for IndexPager::extractResultInfo() |
474 | 'ipb_id', |
475 | 'ipb_timestamp', |
476 | ] + $commentQuery['fields'], |
477 | 'conds' => $this->conds, |
478 | 'join_conds' => [ |
479 | 'ipblocks_by_actor' => [ 'JOIN', 'actor_id=ipb_by_actor' ] |
480 | ] + $commentQuery['joins'] |
481 | ]; |
482 | # Filter out any expired blocks |
483 | $info['conds'][] = $db->expr( 'ipb_expiry', '>', $db->timestamp() ); |
484 | |
485 | # Is the user allowed to see hidden blocks? |
486 | if ( !$this->getAuthority()->isAllowed( 'hideuser' ) ) { |
487 | $info['conds']['ipb_deleted'] = 0; |
488 | } |
489 | } else { |
490 | $commentQuery = $this->commentStore->getJoin( 'bl_reason' ); |
491 | $info = [ |
492 | 'tables' => array_merge( |
493 | [ |
494 | 'block', |
495 | 'block_by_actor' => 'actor', |
496 | 'block_target', |
497 | ], |
498 | $commentQuery['tables'] |
499 | ), |
500 | 'fields' => [ |
501 | // The target fields should be those accepted by BlockUtils::parseBlockTargetRow() |
502 | 'bt_address', |
503 | 'bt_user_text', |
504 | 'bt_user', |
505 | 'bt_auto', |
506 | 'bt_range_start', |
507 | 'bt_range_end', |
508 | // Block fields and aliases |
509 | 'bl_id', |
510 | 'bl_by' => 'block_by_actor.actor_user', |
511 | 'bl_by_text' => 'block_by_actor.actor_name', |
512 | 'bl_timestamp', |
513 | 'bl_anon_only', |
514 | 'bl_create_account', |
515 | 'bl_enable_autoblock', |
516 | 'bl_expiry', |
517 | 'bl_deleted', |
518 | 'bl_block_email', |
519 | 'bl_allow_usertalk', |
520 | 'bl_sitewide', |
521 | ] + $commentQuery['fields'], |
522 | 'conds' => $this->conds, |
523 | 'join_conds' => [ |
524 | 'block_by_actor' => [ 'JOIN', 'actor_id=bl_by_actor' ], |
525 | 'block_target' => [ 'JOIN', 'bt_id=bl_target' ], |
526 | ] + $commentQuery['joins'] |
527 | ]; |
528 | |
529 | # Filter out any expired blocks |
530 | $info['conds'][] = $db->expr( 'bl_expiry', '>', $db->timestamp() ); |
531 | |
532 | # Filter out blocks with the deleted option if the user doesn't |
533 | # have permission to see hidden users |
534 | # TODO: consider removing this -- we could just redact them instead. |
535 | # The mere fact that an admin has deleted a user does not need to |
536 | # be private and could be included in block lists and logs for |
537 | # transparency purposes. Previously, filtering out deleted blocks |
538 | # was a convenient way to avoid showing the target name. |
539 | if ( !$this->getAuthority()->isAllowed( 'hideuser' ) ) { |
540 | $info['conds']['bl_deleted'] = 0; |
541 | } |
542 | |
543 | # Determine if the user is hidden |
544 | # With multiblocks we can't just rely on bl_deleted in the row being formatted |
545 | $info['fields']['hu_deleted'] = $this->hideUserUtils->getExpression( |
546 | $db, 'block_target.bt_user', HideUserUtils::HIDDEN_USERS ); |
547 | } |
548 | return $info; |
549 | } |
550 | |
551 | protected function getTableClass() { |
552 | return parent::getTableClass() . ' mw-blocklist'; |
553 | } |
554 | |
555 | public function getIndexField() { |
556 | if ( $this->readStage === SCHEMA_COMPAT_READ_OLD ) { |
557 | return [ [ 'ipb_timestamp', 'ipb_id' ] ]; |
558 | } else { |
559 | return [ [ 'bl_timestamp', 'bl_id' ] ]; |
560 | } |
561 | } |
562 | |
563 | public function getDefaultSort() { |
564 | return ''; |
565 | } |
566 | |
567 | protected function isFieldSortable( $name ) { |
568 | return false; |
569 | } |
570 | |
571 | /** |
572 | * Do a LinkBatch query to minimise database load when generating all these links |
573 | * @param IResultWrapper $result |
574 | */ |
575 | public function preprocessResults( $result ) { |
576 | // Do a link batch query |
577 | $lb = $this->linkBatchFactory->newLinkBatch(); |
578 | $lb->setCaller( __METHOD__ ); |
579 | |
580 | $partialBlocks = []; |
581 | $userIds = []; |
582 | foreach ( $result as $row ) { |
583 | $target = $row->bt_address ?? $row->bt_user_text; |
584 | if ( $target !== null ) { |
585 | $lb->add( NS_USER, $target ); |
586 | $lb->add( NS_USER_TALK, $target ); |
587 | } |
588 | |
589 | if ( isset( $row->bl_by_text ) ) { |
590 | $lb->add( NS_USER, $row->bl_by_text ); |
591 | $lb->add( NS_USER_TALK, $row->bl_by_text ); |
592 | } |
593 | |
594 | if ( !$row->bl_sitewide ) { |
595 | $partialBlocks[] = (int)$row->bl_id; |
596 | } |
597 | |
598 | if ( $row->bt_user ) { |
599 | $userIds[] = $row->bt_user; |
600 | } |
601 | } |
602 | |
603 | if ( $partialBlocks ) { |
604 | // Mutations to the $row object are not persisted. The restrictions will |
605 | // need be stored in a separate store. |
606 | $this->restrictions = $this->blockRestrictionStore->loadByBlockId( $partialBlocks ); |
607 | |
608 | foreach ( $this->restrictions as $restriction ) { |
609 | if ( $restriction->getType() === PageRestriction::TYPE ) { |
610 | '@phan-var PageRestriction $restriction'; |
611 | $title = $restriction->getTitle(); |
612 | if ( $title ) { |
613 | $lb->addObj( $title ); |
614 | } |
615 | } |
616 | } |
617 | } |
618 | |
619 | $lb->execute(); |
620 | |
621 | // Format comments |
622 | // The keys of formattedComments will be the corresponding offset into $result |
623 | $this->formattedComments = $this->rowCommentFormatter->formatRows( $result, 'bl_reason' ); |
624 | } |
625 | |
626 | } |
627 | |
628 | /** |
629 | * Retain the old class name for backwards compatibility. |
630 | * @deprecated since 1.41 |
631 | */ |
632 | class_alias( BlockListPager::class, 'BlockListPager' ); |