MediaWiki REL1_37
BlockListPager.php
Go to the documentation of this file.
1<?php
32use Wikimedia\IPUtils;
35
40
41 protected $conds;
42
48 protected $restrictions = [];
49
52
55
58
61
63 private $blockUtils;
64
67
79 public function __construct(
80 $page,
81 $conds,
84 ILoadBalancer $loadBalancer,
89 ) {
90 $this->mDb = $loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
91 parent::__construct( $page->getContext(), $page->getLinkRenderer() );
92 $this->conds = $conds;
93 $this->mDefaultDirection = IndexPager::DIR_DESCENDING;
94 $this->linkBatchFactory = $linkBatchFactory;
95 $this->blockRestrictionStore = $blockRestrictionStore;
96 $this->specialPageFactory = $specialPageFactory;
97 $this->commentStore = $commentStore;
98 $this->blockUtils = $blockUtils;
99 $this->blockActionInfo = $blockActionInfo;
100 }
101
102 protected function getFieldNames() {
103 static $headers = null;
104
105 if ( $headers === null ) {
106 $headers = [
107 'ipb_timestamp' => 'blocklist-timestamp',
108 'ipb_target' => 'blocklist-target',
109 'ipb_expiry' => 'blocklist-expiry',
110 'ipb_by' => 'blocklist-by',
111 'ipb_params' => 'blocklist-params',
112 'ipb_reason' => 'blocklist-reason',
113 ];
114 foreach ( $headers as $key => $val ) {
115 $headers[$key] = $this->msg( $val )->text();
116 }
117 }
118
119 return $headers;
120 }
121
128 public function formatValue( $name, $value ) {
129 static $msg = null;
130 if ( $msg === null ) {
131 $keys = [
132 'anononlyblock',
133 'createaccountblock',
134 'noautoblockblock',
135 'emailblock',
136 'blocklist-nousertalk',
137 'unblocklink',
138 'change-blocklink',
139 'blocklist-editing',
140 'blocklist-editing-sitewide',
141 ];
142
143 foreach ( $keys as $key ) {
144 $msg[$key] = $this->msg( $key )->text();
145 }
146 }
147 '@phan-var string[] $msg';
148
150 $row = $this->mCurrentRow;
151
152 $language = $this->getLanguage();
153
154 $formatted = '';
155
157
158 switch ( $name ) {
159 case 'ipb_timestamp':
160 $formatted = htmlspecialchars( $language->userTimeAndDate( $value, $this->getUser() ) );
161 break;
162
163 case 'ipb_target':
164 if ( $row->ipb_auto ) {
165 $formatted = $this->msg( 'autoblockid', $row->ipb_id )->parse();
166 } else {
167 list( $target, ) = $this->blockUtils->parseBlockTarget( $row->ipb_address );
168
169 if ( is_string( $target ) ) {
170 if ( IPUtils::isValidRange( $target ) ) {
171 $target = User::newFromName( $target, false );
172 } else {
173 $formatted = $target;
174 }
175 }
176
177 if ( $target instanceof UserIdentity ) {
178 $formatted = Linker::userLink( $target->getId(), $target->getName() );
179 $formatted .= Linker::userToolLinks(
180 $target->getId(),
181 $target->getName(),
182 false,
184 );
185 }
186 }
187 break;
188
189 case 'ipb_expiry':
190 $formatted = htmlspecialchars( $language->formatExpiry(
191 $value,
192 /* User preference timezone */true,
193 'infinity',
194 $this->getUser()
195 ) );
196 if ( $this->getAuthority()->isAllowed( 'block' ) ) {
197 $links = [];
198 if ( $row->ipb_auto ) {
199 $links[] = $linkRenderer->makeKnownLink(
200 $this->specialPageFactory->getTitleForAlias( 'Unblock' ),
201 $msg['unblocklink'],
202 [],
203 [ 'wpTarget' => "#{$row->ipb_id}" ]
204 );
205 } else {
206 $links[] = $linkRenderer->makeKnownLink(
207 $this->specialPageFactory->getTitleForAlias( 'Unblock/' . $row->ipb_address ),
208 $msg['unblocklink']
209 );
210 $links[] = $linkRenderer->makeKnownLink(
211 $this->specialPageFactory->getTitleForAlias( 'Block/' . $row->ipb_address ),
212 $msg['change-blocklink']
213 );
214 }
215 $formatted .= ' ' . Html::rawElement(
216 'span',
217 [ 'class' => 'mw-blocklist-actions' ],
218 $this->msg( 'parentheses' )->rawParams(
219 $language->pipeList( $links ) )->escaped()
220 );
221 }
222 if ( $value !== 'infinity' ) {
223 $timestamp = new MWTimestamp( $value );
224 $formatted .= '<br />' . $this->msg(
225 'ipb-blocklist-duration-left',
226 $language->formatDuration(
227 $timestamp->getTimestamp() - MWTimestamp::time(),
228 // reasonable output
229 [
230 'minutes',
231 'hours',
232 'days',
233 'years',
234 ]
235 )
236 )->escaped();
237 }
238 break;
239
240 case 'ipb_by':
241 $formatted = Linker::userLink( $value, $row->ipb_by_text );
242 $formatted .= Linker::userToolLinks( $value, $row->ipb_by_text );
243 break;
244
245 case 'ipb_reason':
246 $value = $this->commentStore->getComment( 'ipb_reason', $row )->text;
247 $formatted = Linker::formatComment( $value );
248 break;
249
250 case 'ipb_params':
251 $properties = [];
252
253 if ( $row->ipb_sitewide ) {
254 $properties[] = htmlspecialchars( $msg['blocklist-editing-sitewide'] );
255 }
256
257 if ( !$row->ipb_sitewide && $this->restrictions ) {
258 $list = $this->getRestrictionListHTML( $row );
259 if ( $list ) {
260 $properties[] = htmlspecialchars( $msg['blocklist-editing'] ) . $list;
261 }
262 }
263
264 if ( $row->ipb_anon_only ) {
265 $properties[] = htmlspecialchars( $msg['anononlyblock'] );
266 }
267 if ( $row->ipb_create_account ) {
268 $properties[] = htmlspecialchars( $msg['createaccountblock'] );
269 }
270 if ( $row->ipb_user && !$row->ipb_enable_autoblock ) {
271 $properties[] = htmlspecialchars( $msg['noautoblockblock'] );
272 }
273
274 if ( $row->ipb_block_email ) {
275 $properties[] = htmlspecialchars( $msg['emailblock'] );
276 }
277
278 if ( !$row->ipb_allow_usertalk ) {
279 $properties[] = htmlspecialchars( $msg['blocklist-nousertalk'] );
280 }
281
282 $formatted = Html::rawElement(
283 'ul',
284 [],
285 implode( '', array_map( static function ( $prop ) {
286 return Html::rawElement(
287 'li',
288 [],
289 $prop
290 );
291 }, $properties ) )
292 );
293 break;
294
295 default:
296 $formatted = "Unable to format $name";
297 break;
298 }
299
300 return $formatted;
301 }
302
310 private function getRestrictionListHTML( stdClass $row ) {
311 $items = [];
313
314 foreach ( $this->restrictions as $restriction ) {
315 if ( $restriction->getBlockId() !== (int)$row->ipb_id ) {
316 continue;
317 }
318
319 switch ( $restriction->getType() ) {
320 case PageRestriction::TYPE:
321 '@phan-var PageRestriction $restriction';
322 if ( $restriction->getTitle() ) {
323 $items[$restriction->getType()][] = Html::rawElement(
324 'li',
325 [],
326 $linkRenderer->makeLink( $restriction->getTitle() )
327 );
328 }
329 break;
330 case NamespaceRestriction::TYPE:
331 $text = $restriction->getValue() === NS_MAIN
332 ? $this->msg( 'blanknamespace' )->text()
333 : $this->getLanguage()->getFormattedNsText(
334 $restriction->getValue()
335 );
336 if ( $text ) {
337 $items[$restriction->getType()][] = Html::rawElement(
338 'li',
339 [],
341 $this->specialPageFactory->getTitleForAlias( 'Allpages' ),
342 $text,
343 [],
344 [
345 'namespace' => $restriction->getValue()
346 ]
347 )
348 );
349 }
350 break;
351 case ActionRestriction::TYPE:
352 $actionName = $this->blockActionInfo->getActionFromId( $restriction->getValue() );
353 $enablePartialActionBlocks = $this->getConfig()->get( 'EnablePartialActionBlocks' );
354 if ( $actionName && $enablePartialActionBlocks ) {
355 $items[$restriction->getType()][] = Html::rawElement(
356 'li',
357 [],
358 $this->msg( 'ipb-action-' .
359 $this->blockActionInfo->getActionFromId( $restriction->getValue() ) )->escaped()
360 );
361 }
362 break;
363 }
364 }
365
366 if ( empty( $items ) ) {
367 return '';
368 }
369
370 $sets = [];
371 foreach ( $items as $key => $value ) {
372 $sets[] = Html::rawElement(
373 'li',
374 [],
375 $this->msg( 'blocklist-editing-' . $key ) . Html::rawElement(
376 'ul',
377 [],
378 implode( '', $value )
379 )
380 );
381 }
382
383 return Html::rawElement(
384 'ul',
385 [],
386 implode( '', $sets )
387 );
388 }
389
390 public function getQueryInfo() {
391 $commentQuery = $this->commentStore->getJoin( 'ipb_reason' );
392
393 $info = [
394 'tables' => array_merge(
395 [ 'ipblocks', 'ipblocks_by_actor' => 'actor' ],
396 $commentQuery['tables']
397 ),
398 'fields' => [
399 'ipb_id',
400 'ipb_address',
401 'ipb_user',
402 'ipb_by' => 'ipblocks_by_actor.actor_user',
403 'ipb_by_text' => 'ipblocks_by_actor.actor_name',
404 'ipb_timestamp',
405 'ipb_auto',
406 'ipb_anon_only',
407 'ipb_create_account',
408 'ipb_enable_autoblock',
409 'ipb_expiry',
410 'ipb_range_start',
411 'ipb_range_end',
412 'ipb_deleted',
413 'ipb_block_email',
414 'ipb_allow_usertalk',
415 'ipb_sitewide',
416 ] + $commentQuery['fields'],
417 'conds' => $this->conds,
418 'join_conds' => [
419 'ipblocks_by_actor' => [ 'JOIN', 'actor_id=ipb_by_actor' ]
420 ] + $commentQuery['joins']
421 ];
422
423 # Filter out any expired blocks
424 $db = $this->getDatabase();
425 $info['conds'][] = 'ipb_expiry > ' . $db->addQuotes( $db->timestamp() );
426
427 # Is the user allowed to see hidden blocks?
428 if ( !$this->getAuthority()->isAllowed( 'hideuser' ) ) {
429 $info['conds']['ipb_deleted'] = 0;
430 }
431
432 return $info;
433 }
434
440 public function getTotalAutoblocks() {
441 $dbr = $this->getDatabase();
442 return (int)$dbr->selectField( 'ipblocks', 'COUNT(*)',
443 [
444 'ipb_auto' => '1',
445 'ipb_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() ),
446 ],
447 __METHOD__
448 );
449 }
450
451 protected function getTableClass() {
452 return parent::getTableClass() . ' mw-blocklist';
453 }
454
455 public function getIndexField() {
456 return [ [ 'ipb_timestamp', 'ipb_id' ] ];
457 }
458
459 public function getDefaultSort() {
460 return '';
461 }
462
463 protected function isFieldSortable( $name ) {
464 return false;
465 }
466
471 public function preprocessResults( $result ) {
472 # Do a link batch query
473 $lb = $this->linkBatchFactory->newLinkBatch();
474 $lb->setCaller( __METHOD__ );
475
476 $partialBlocks = [];
477 foreach ( $result as $row ) {
478 $lb->add( NS_USER, $row->ipb_address );
479 $lb->add( NS_USER_TALK, $row->ipb_address );
480
481 if ( $row->ipb_by ?? null ) {
482 $lb->add( NS_USER, $row->ipb_by_text );
483 $lb->add( NS_USER_TALK, $row->ipb_by_text );
484 }
485
486 if ( !$row->ipb_sitewide ) {
487 $partialBlocks[] = $row->ipb_id;
488 }
489 }
490
491 if ( $partialBlocks ) {
492 // Mutations to the $row object are not persisted. The restrictions will
493 // need be stored in a separate store.
494 $this->restrictions = $this->blockRestrictionStore->loadByBlockId( $partialBlocks );
495
496 foreach ( $this->restrictions as $restriction ) {
497 if ( $restriction->getType() === PageRestriction::TYPE ) {
498 '@phan-var PageRestriction $restriction';
499 $title = $restriction->getTitle();
500 if ( $title ) {
501 $lb->addObj( $title );
502 }
503 }
504 }
505 }
506
507 $lb->execute();
508 }
509
510}
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.
SpecialPageFactory $specialPageFactory
isFieldSortable( $name)
Return true if the named field should be sortable by the UI, false otherwise.
BlockUtils $blockUtils
getTableClass()
TablePager relies on mw-datatable for styling, see T214208.
__construct( $page, $conds, LinkBatchFactory $linkBatchFactory, BlockRestrictionStore $blockRestrictionStore, ILoadBalancer $loadBalancer, SpecialPageFactory $specialPageFactory, CommentStore $commentStore, BlockUtils $blockUtils, BlockActionInfo $blockActionInfo)
CommentStore $commentStore
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.
getRestrictionListHTML(stdClass $row)
Get Restriction List HTML.
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.
BlockRestrictionStore $blockRestrictionStore
LinkBatchFactory $linkBatchFactory
BlockActionInfo $blockActionInfo
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()
getDatabase()
Get the Database object in use.
LinkRenderer $linkRenderer
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:1064
const TOOL_LINKS_NOBLOCK
Flags for userToolLinks()
Definition Linker.php:42
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:1109
static formatComment( $comment, $title=null, $local=false, $wikiId=null)
This function is called by all recent changes variants, by the page history, and by the user contribu...
Definition Linker.php:1372
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.
makeKnownLink( $target, $text=null, array $extraAttribs=[], array $query=[])
makeLink( $target, $text=null, array $extraAttribs=[], array $query=[])
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:607
Interface for objects representing user identity.
Database cluster connection, tracking, load balancing, and transaction manager interface.
getConnectionRef( $i, $groups=[], $domain=false, $flags=0)
Get a live database handle reference for a server index.
Result wrapper for grabbing data queried from an IDatabase object.