MediaWiki REL1_39
BlockListPager.php
Go to the documentation of this file.
1<?php
35use Wikimedia\IPUtils;
38
43
44 protected $conds;
45
51 protected $restrictions = [];
52
54 private $blockActionInfo;
55
57 private $blockRestrictionStore;
58
60 private $blockUtils;
61
63 private $commentStore;
64
66 private $linkBatchFactory;
67
69 private $rowCommentFormatter;
70
72 private $specialPageFactory;
73
75 private $formattedComments = [];
76
90 public function __construct(
91 IContextSource $context,
92 BlockActionInfo $blockActionInfo,
93 BlockRestrictionStore $blockRestrictionStore,
94 BlockUtils $blockUtils,
95 CommentStore $commentStore,
96 LinkBatchFactory $linkBatchFactory,
97 LinkRenderer $linkRenderer,
98 ILoadBalancer $loadBalancer,
99 RowCommentFormatter $rowCommentFormatter,
100 SpecialPageFactory $specialPageFactory,
101 $conds
102 ) {
103 // Set database before parent constructor to avoid setting it there with wfGetDB
104 $this->mDb = $loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
105 parent::__construct( $context, $linkRenderer );
106 $this->blockActionInfo = $blockActionInfo;
107 $this->blockRestrictionStore = $blockRestrictionStore;
108 $this->blockUtils = $blockUtils;
109 $this->commentStore = $commentStore;
110 $this->linkBatchFactory = $linkBatchFactory;
111 $this->rowCommentFormatter = $rowCommentFormatter;
112 $this->specialPageFactory = $specialPageFactory;
113 $this->conds = $conds;
114 $this->mDefaultDirection = IndexPager::DIR_DESCENDING;
115 }
116
117 protected function getFieldNames() {
118 static $headers = null;
119
120 if ( $headers === null ) {
121 $headers = [
122 'ipb_timestamp' => 'blocklist-timestamp',
123 'ipb_target' => 'blocklist-target',
124 'ipb_expiry' => 'blocklist-expiry',
125 'ipb_by' => 'blocklist-by',
126 'ipb_params' => 'blocklist-params',
127 'ipb_reason' => 'blocklist-reason',
128 ];
129 foreach ( $headers as $key => $val ) {
130 $headers[$key] = $this->msg( $val )->text();
131 }
132 }
133
134 return $headers;
135 }
136
143 public function formatValue( $name, $value ) {
144 static $msg = null;
145 if ( $msg === null ) {
146 $keys = [
147 'anononlyblock',
148 'createaccountblock',
149 'noautoblockblock',
150 'emailblock',
151 'blocklist-nousertalk',
152 'unblocklink',
153 'change-blocklink',
154 'blocklist-editing',
155 'blocklist-editing-sitewide',
156 ];
157
158 foreach ( $keys as $key ) {
159 $msg[$key] = $this->msg( $key )->text();
160 }
161 }
162 '@phan-var string[] $msg';
163
165 $row = $this->mCurrentRow;
166
167 $language = $this->getLanguage();
168
169 $formatted = '';
170
171 $linkRenderer = $this->getLinkRenderer();
172
173 switch ( $name ) {
174 case 'ipb_timestamp':
175 $formatted = htmlspecialchars( $language->userTimeAndDate( $value, $this->getUser() ) );
176 break;
177
178 case 'ipb_target':
179 if ( $row->ipb_auto ) {
180 $formatted = $this->msg( 'autoblockid', $row->ipb_id )->parse();
181 } else {
182 list( $target, ) = $this->blockUtils->parseBlockTarget( $row->ipb_address );
183
184 if ( is_string( $target ) ) {
185 if ( IPUtils::isValidRange( $target ) ) {
186 $target = User::newFromName( $target, false );
187 } else {
188 $formatted = $target;
189 }
190 }
191
192 if ( $target instanceof UserIdentity ) {
193 $formatted = Linker::userLink( $target->getId(), $target->getName() );
194 $formatted .= Linker::userToolLinks(
195 $target->getId(),
196 $target->getName(),
197 false,
199 );
200 }
201 }
202 break;
203
204 case 'ipb_expiry':
205 $formatted = htmlspecialchars( $language->formatExpiry(
206 $value,
207 /* User preference timezone */true,
208 'infinity',
209 $this->getUser()
210 ) );
211 if ( $this->getAuthority()->isAllowed( 'block' ) ) {
212 $links = [];
213 if ( $row->ipb_auto ) {
214 $links[] = $linkRenderer->makeKnownLink(
215 $this->specialPageFactory->getTitleForAlias( 'Unblock' ),
216 $msg['unblocklink'],
217 [],
218 [ 'wpTarget' => "#{$row->ipb_id}" ]
219 );
220 } else {
221 $links[] = $linkRenderer->makeKnownLink(
222 $this->specialPageFactory->getTitleForAlias( 'Unblock/' . $row->ipb_address ),
223 $msg['unblocklink']
224 );
225 $links[] = $linkRenderer->makeKnownLink(
226 $this->specialPageFactory->getTitleForAlias( 'Block/' . $row->ipb_address ),
227 $msg['change-blocklink']
228 );
229 }
230 $formatted .= ' ' . Html::rawElement(
231 'span',
232 [ 'class' => 'mw-blocklist-actions' ],
233 $this->msg( 'parentheses' )->rawParams(
234 $language->pipeList( $links ) )->escaped()
235 );
236 }
237 if ( $value !== 'infinity' ) {
238 $timestamp = new MWTimestamp( $value );
239 $formatted .= '<br />' . $this->msg(
240 'ipb-blocklist-duration-left',
241 $language->formatDuration(
242 (int)$timestamp->getTimestamp( TS_UNIX ) - MWTimestamp::time(),
243 // reasonable output
244 [
245 'minutes',
246 'hours',
247 'days',
248 'years',
249 ]
250 )
251 )->escaped();
252 }
253 break;
254
255 case 'ipb_by':
256 $formatted = Linker::userLink( (int)$value, $row->ipb_by_text );
257 $formatted .= Linker::userToolLinks( (int)$value, $row->ipb_by_text );
258 break;
259
260 case 'ipb_reason':
261 $formatted = $this->formattedComments[$this->getResultOffset()];
262 break;
263
264 case 'ipb_params':
265 $properties = [];
266
267 if ( $row->ipb_sitewide ) {
268 $properties[] = htmlspecialchars( $msg['blocklist-editing-sitewide'] );
269 }
270
271 if ( !$row->ipb_sitewide && $this->restrictions ) {
272 $list = $this->getRestrictionListHTML( $row );
273 if ( $list ) {
274 $properties[] = htmlspecialchars( $msg['blocklist-editing'] ) . $list;
275 }
276 }
277
278 if ( $row->ipb_anon_only ) {
279 $properties[] = htmlspecialchars( $msg['anononlyblock'] );
280 }
281 if ( $row->ipb_create_account ) {
282 $properties[] = htmlspecialchars( $msg['createaccountblock'] );
283 }
284 if ( $row->ipb_user && !$row->ipb_enable_autoblock ) {
285 $properties[] = htmlspecialchars( $msg['noautoblockblock'] );
286 }
287
288 if ( $row->ipb_block_email ) {
289 $properties[] = htmlspecialchars( $msg['emailblock'] );
290 }
291
292 if ( !$row->ipb_allow_usertalk ) {
293 $properties[] = htmlspecialchars( $msg['blocklist-nousertalk'] );
294 }
295
296 $formatted = Html::rawElement(
297 'ul',
298 [],
299 implode( '', array_map( static function ( $prop ) {
300 return Html::rawElement(
301 'li',
302 [],
303 $prop
304 );
305 }, $properties ) )
306 );
307 break;
308
309 default:
310 $formatted = "Unable to format $name";
311 break;
312 }
313
314 return $formatted;
315 }
316
324 private function getRestrictionListHTML( stdClass $row ) {
325 $items = [];
326 $linkRenderer = $this->getLinkRenderer();
327
328 foreach ( $this->restrictions as $restriction ) {
329 if ( $restriction->getBlockId() !== (int)$row->ipb_id ) {
330 continue;
331 }
332
333 switch ( $restriction->getType() ) {
334 case PageRestriction::TYPE:
335 '@phan-var PageRestriction $restriction';
336 if ( $restriction->getTitle() ) {
337 $items[$restriction->getType()][] = Html::rawElement(
338 'li',
339 [],
340 $linkRenderer->makeLink( $restriction->getTitle() )
341 );
342 }
343 break;
344 case NamespaceRestriction::TYPE:
345 $text = $restriction->getValue() === NS_MAIN
346 ? $this->msg( 'blanknamespace' )->text()
347 : $this->getLanguage()->getFormattedNsText(
348 $restriction->getValue()
349 );
350 if ( $text ) {
351 $items[$restriction->getType()][] = Html::rawElement(
352 'li',
353 [],
354 $linkRenderer->makeLink(
355 $this->specialPageFactory->getTitleForAlias( 'Allpages' ),
356 $text,
357 [],
358 [
359 'namespace' => $restriction->getValue()
360 ]
361 )
362 );
363 }
364 break;
365 case ActionRestriction::TYPE:
366 $actionName = $this->blockActionInfo->getActionFromId( $restriction->getValue() );
367 $enablePartialActionBlocks =
368 $this->getConfig()->get( MainConfigNames::EnablePartialActionBlocks );
369 if ( $actionName && $enablePartialActionBlocks ) {
370 $items[$restriction->getType()][] = Html::rawElement(
371 'li',
372 [],
373 $this->msg( 'ipb-action-' .
374 $this->blockActionInfo->getActionFromId( $restriction->getValue() ) )->escaped()
375 );
376 }
377 break;
378 }
379 }
380
381 if ( empty( $items ) ) {
382 return '';
383 }
384
385 $sets = [];
386 foreach ( $items as $key => $value ) {
387 $sets[] = Html::rawElement(
388 'li',
389 [],
390 $this->msg( 'blocklist-editing-' . $key ) . Html::rawElement(
391 'ul',
392 [],
393 implode( '', $value )
394 )
395 );
396 }
397
398 return Html::rawElement(
399 'ul',
400 [],
401 implode( '', $sets )
402 );
403 }
404
405 public function getQueryInfo() {
406 $commentQuery = $this->commentStore->getJoin( 'ipb_reason' );
407
408 $info = [
409 'tables' => array_merge(
410 [ 'ipblocks', 'ipblocks_by_actor' => 'actor' ],
411 $commentQuery['tables']
412 ),
413 'fields' => [
414 'ipb_id',
415 'ipb_address',
416 'ipb_user',
417 'ipb_by' => 'ipblocks_by_actor.actor_user',
418 'ipb_by_text' => 'ipblocks_by_actor.actor_name',
419 'ipb_timestamp',
420 'ipb_auto',
421 'ipb_anon_only',
422 'ipb_create_account',
423 'ipb_enable_autoblock',
424 'ipb_expiry',
425 'ipb_range_start',
426 'ipb_range_end',
427 'ipb_deleted',
428 'ipb_block_email',
429 'ipb_allow_usertalk',
430 'ipb_sitewide',
431 ] + $commentQuery['fields'],
432 'conds' => $this->conds,
433 'join_conds' => [
434 'ipblocks_by_actor' => [ 'JOIN', 'actor_id=ipb_by_actor' ]
435 ] + $commentQuery['joins']
436 ];
437
438 # Filter out any expired blocks
439 $db = $this->getDatabase();
440 $info['conds'][] = 'ipb_expiry > ' . $db->addQuotes( $db->timestamp() );
441
442 # Is the user allowed to see hidden blocks?
443 if ( !$this->getAuthority()->isAllowed( 'hideuser' ) ) {
444 $info['conds']['ipb_deleted'] = 0;
445 }
446
447 return $info;
448 }
449
455 public function getTotalAutoblocks() {
456 $dbr = $this->getDatabase();
457 return (int)$dbr->selectField( 'ipblocks', 'COUNT(*)',
458 [
459 'ipb_auto' => '1',
460 'ipb_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() ),
461 ],
462 __METHOD__
463 );
464 }
465
466 protected function getTableClass() {
467 return parent::getTableClass() . ' mw-blocklist';
468 }
469
470 public function getIndexField() {
471 return [ [ 'ipb_timestamp', 'ipb_id' ] ];
472 }
473
474 public function getDefaultSort() {
475 return '';
476 }
477
478 protected function isFieldSortable( $name ) {
479 return false;
480 }
481
486 public function preprocessResults( $result ) {
487 // Do a link batch query
488 $lb = $this->linkBatchFactory->newLinkBatch();
489 $lb->setCaller( __METHOD__ );
490
491 $partialBlocks = [];
492 foreach ( $result as $row ) {
493 $lb->add( NS_USER, $row->ipb_address );
494 $lb->add( NS_USER_TALK, $row->ipb_address );
495
496 if ( $row->ipb_by ?? null ) {
497 $lb->add( NS_USER, $row->ipb_by_text );
498 $lb->add( NS_USER_TALK, $row->ipb_by_text );
499 }
500
501 if ( !$row->ipb_sitewide ) {
502 $partialBlocks[] = $row->ipb_id;
503 }
504 }
505
506 if ( $partialBlocks ) {
507 // Mutations to the $row object are not persisted. The restrictions will
508 // need be stored in a separate store.
509 $this->restrictions = $this->blockRestrictionStore->loadByBlockId( $partialBlocks );
510
511 foreach ( $this->restrictions as $restriction ) {
512 if ( $restriction->getType() === PageRestriction::TYPE ) {
513 '@phan-var PageRestriction $restriction';
514 $title = $restriction->getTitle();
515 if ( $title ) {
516 $lb->addObj( $title );
517 }
518 }
519 }
520 }
521
522 $lb->execute();
523
524 // Format comments
525 // The keys of formattedComments will be the corresponding offset into $result
526 $this->formattedComments = $this->rowCommentFormatter->formatRows( $result, 'ipb_reason' );
527 }
528
529}
const NS_USER
Definition Defines.php:66
const NS_MAIN
Definition Defines.php:64
const NS_USER_TALK
Definition Defines.php:67
getQueryInfo()
Provides all parameters needed for the main paged query.
isFieldSortable( $name)
Return true if the named field should be sortable by the UI, false otherwise.
getTableClass()
TablePager relies on mw-datatable for styling, see T214208.
getFieldNames()
An array mapping database field names to a textual description of the field name, for use in the tabl...
getIndexField()
Returns the name of the index field.If the pager supports multiple orders, it may return an array of ...
preprocessResults( $result)
Do a LinkBatch query to minimise database load when generating all these links.
__construct(IContextSource $context, BlockActionInfo $blockActionInfo, BlockRestrictionStore $blockRestrictionStore, BlockUtils $blockUtils, CommentStore $commentStore, LinkBatchFactory $linkBatchFactory, LinkRenderer $linkRenderer, ILoadBalancer $loadBalancer, RowCommentFormatter $rowCommentFormatter, SpecialPageFactory $specialPageFactory, $conds)
formatValue( $name, $value)
Restriction[] $restrictions
Array of restrictions.
getTotalAutoblocks()
Get total number of autoblocks at any given time.
getDefaultSort()
The database field name used as a default sort order.
Handle database storage of comments such as edit summaries and log reasons.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition Html.php:214
getDatabase()
Get the Database object in use.
const DIR_DESCENDING
Backwards-compatible constant for $mDefaultDirection field (do not change)
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition Linker.php:1114
const TOOL_LINKS_NOBLOCK
Flags for userToolLinks()
Definition Linker.php:46
static userToolLinks( $userId, $userText, $redContribsWhenNoEdits=false, $flags=0, $edits=null, $useParentheses=true)
Generate standard user tool links (talk, contributions, block link, etc.)
Definition Linker.php:1159
Library for creating and parsing MW-style timestamps.
Defines the actions that can be blocked by a partial block.
Backend class for blocking utils.
Restriction for partial blocks of actions.
This is basically a CommentFormatter with a CommentStore dependency, allowing it to retrieve comment ...
Class that generates HTML anchor link elements for pages.
A class containing constants representing the names of configuration variables.
Factory for handling the special page list and generating SpecialPage objects.
Table-based display with a user-selectable sort order.
stdClass $mCurrentRow
static newFromName( $name, $validate='valid')
Definition User.php:598
Interface for objects which can provide a MediaWiki context on request.
Interface for objects representing user identity.
Create and track the database connections and transactions for a given database cluster.
getConnectionRef( $i, $groups=[], $domain=false, $flags=0)
Result wrapper for grabbing data queried from an IDatabase object.