MediaWiki  master
BlockListPager.php
Go to the documentation of this file.
1 <?php
32 use Wikimedia\IPUtils;
35 
39 class BlockListPager extends TablePager {
40 
41  protected $conds;
42 
48  protected $restrictions = [];
49 
52 
55 
58 
60  private $commentStore;
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 
156  $linkRenderer = $this->getLinkRenderer();
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 = [];
312  $linkRenderer = $this->getLinkRenderer();
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  [],
340  $linkRenderer->makeLink(
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  $res = $dbr->selectField( 'ipblocks',
443  'COUNT(*)',
444  [
445  'ipb_auto' => '1',
446  'ipb_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() ),
447  ],
448  __METHOD__
449  );
450  if ( $res ) {
451  return $res;
452  }
453  return 0; // We found nothing
454  }
455 
456  protected function getTableClass() {
457  return parent::getTableClass() . ' mw-blocklist';
458  }
459 
460  public function getIndexField() {
461  return [ [ 'ipb_timestamp', 'ipb_id' ] ];
462  }
463 
464  public function getDefaultSort() {
465  return '';
466  }
467 
468  protected function isFieldSortable( $name ) {
469  return false;
470  }
471 
476  public function preprocessResults( $result ) {
477  # Do a link batch query
478  $lb = $this->linkBatchFactory->newLinkBatch();
479  $lb->setCaller( __METHOD__ );
480 
481  $partialBlocks = [];
482  foreach ( $result as $row ) {
483  $lb->add( NS_USER, $row->ipb_address );
484  $lb->add( NS_USER_TALK, $row->ipb_address );
485 
486  if ( $row->ipb_by ?? null ) {
487  $lb->add( NS_USER, $row->ipb_by_text );
488  $lb->add( NS_USER_TALK, $row->ipb_by_text );
489  }
490 
491  if ( !$row->ipb_sitewide ) {
492  $partialBlocks[] = $row->ipb_id;
493  }
494  }
495 
496  if ( $partialBlocks ) {
497  // Mutations to the $row object are not persisted. The restrictions will
498  // need be stored in a separate store.
499  $this->restrictions = $this->blockRestrictionStore->loadByBlockId( $partialBlocks );
500 
501  foreach ( $this->restrictions as $restriction ) {
502  if ( $restriction->getType() === PageRestriction::TYPE ) {
503  '@phan-var PageRestriction $restriction';
504  $title = $restriction->getTitle();
505  if ( $title ) {
506  $lb->addObj( $title );
507  }
508  }
509  }
510  }
511 
512  $lb->execute();
513  }
514 
515 }
MWTimestamp
Library for creating and parsing MW-style timestamps.
Definition: MWTimestamp.php:38
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:72
Linker\TOOL_LINKS_NOBLOCK
const TOOL_LINKS_NOBLOCK
Flags for userToolLinks()
Definition: Linker.php:41
BlockListPager\isFieldSortable
isFieldSortable( $name)
Return true if the named field should be sortable by the UI, false otherwise.
Definition: BlockListPager.php:468
Linker\userLink
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:1068
BlockListPager
Definition: BlockListPager.php:39
MediaWiki\Block\BlockUtils
Backend class for blocking utils.
Definition: BlockUtils.php:45
MediaWiki\SpecialPage\SpecialPageFactory
Factory for handling the special page list and generating SpecialPage objects.
Definition: SpecialPageFactory.php:63
Linker\userToolLinks
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:1113
BlockListPager\$blockRestrictionStore
BlockRestrictionStore $blockRestrictionStore
Definition: BlockListPager.php:54
IndexPager\getDatabase
getDatabase()
Get the Database object in use.
Definition: IndexPager.php:244
CommentStore
Handle database storage of comments such as edit summaries and log reasons.
Definition: CommentStore.php:42
BlockListPager\getRestrictionListHTML
getRestrictionListHTML(stdClass $row)
Get Restriction List HTML.
Definition: BlockListPager.php:310
User\newFromName
static newFromName( $name, $validate='valid')
Definition: User.php:602
BlockListPager\$conds
$conds
Definition: BlockListPager.php:41
IndexPager\$linkRenderer
LinkRenderer $linkRenderer
Definition: IndexPager.php:167
$res
$res
Definition: testCompression.php:57
Wikimedia\Rdbms\ILoadBalancer\getConnectionRef
getConnectionRef( $i, $groups=[], $domain=false, $flags=0)
Get a live database handle reference for a server index.
BlockListPager\$blockActionInfo
BlockActionInfo $blockActionInfo
Definition: BlockListPager.php:66
BlockListPager\$linkBatchFactory
LinkBatchFactory $linkBatchFactory
Definition: BlockListPager.php:51
MediaWiki\User\UserIdentity
Interface for objects representing user identity.
Definition: UserIdentity.php:39
NS_MAIN
const NS_MAIN
Definition: Defines.php:64
$dbr
$dbr
Definition: testCompression.php:54
ContextSource\getLanguage
getLanguage()
Definition: ContextSource.php:153
BlockListPager\$blockUtils
BlockUtils $blockUtils
Definition: BlockListPager.php:63
BlockListPager\$commentStore
CommentStore $commentStore
Definition: BlockListPager.php:60
Wikimedia\Rdbms\IResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: IResultWrapper.php:24
MediaWiki\Cache\LinkBatchFactory
Definition: LinkBatchFactory.php:39
BlockListPager\getTableClass
getTableClass()
TablePager relies on mw-datatable for styling, see T214208.
Definition: BlockListPager.php:456
TablePager
Table-based display with a user-selectable sort order.
Definition: TablePager.php:31
MediaWiki\Block\Restriction\Restriction
Definition: Restriction.php:25
TablePager\$mCurrentRow
stdClass $mCurrentRow
Definition: TablePager.php:36
BlockListPager\getFieldNames
getFieldNames()
An array mapping database field names to a textual description of the field name, for use in the tabl...
Definition: BlockListPager.php:102
BlockListPager\$restrictions
Restriction[] $restrictions
Array of restrictions.
Definition: BlockListPager.php:48
BlockListPager\getDefaultSort
getDefaultSort()
The database field name used as a default sort order.
Definition: BlockListPager.php:464
$title
$title
Definition: testCompression.php:38
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
ContextSource\msg
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:197
BlockListPager\getIndexField
getIndexField()
Returns the name of the index field.If the pager supports multiple orders, it may return an array of ...
Definition: BlockListPager.php:460
ContextSource\getAuthority
getAuthority()
Definition: ContextSource.php:144
BlockListPager\getQueryInfo
getQueryInfo()
Provides all parameters needed for the main paged query.
Definition: BlockListPager.php:390
BlockListPager\getTotalAutoblocks
getTotalAutoblocks()
Get total number of autoblocks at any given time.
Definition: BlockListPager.php:440
MediaWiki\Block\BlockActionInfo
Defines the actions that can be blocked by a partial block.
Definition: BlockActionInfo.php:40
IndexPager\getLinkRenderer
getLinkRenderer()
Definition: IndexPager.php:980
NS_USER
const NS_USER
Definition: Defines.php:66
Linker\formatComment
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:1376
MediaWiki\Block\Restriction\NamespaceRestriction
Definition: NamespaceRestriction.php:25
IndexPager\DIR_DESCENDING
const DIR_DESCENDING
Backwards-compatible constant for $mDefaultDirection field (do not change)
Definition: IndexPager.php:80
MediaWiki\Block\Restriction\PageRestriction
Definition: PageRestriction.php:25
BlockListPager\preprocessResults
preprocessResults( $result)
Do a LinkBatch query to minimise database load when generating all these links.
Definition: BlockListPager.php:476
BlockListPager\__construct
__construct( $page, $conds, LinkBatchFactory $linkBatchFactory, BlockRestrictionStore $blockRestrictionStore, ILoadBalancer $loadBalancer, SpecialPageFactory $specialPageFactory, CommentStore $commentStore, BlockUtils $blockUtils, BlockActionInfo $blockActionInfo)
Definition: BlockListPager.php:79
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:212
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:67
$keys
$keys
Definition: testCompression.php:72
MediaWiki\Block\Restriction\ActionRestriction
Restriction for partial blocks of actions.
Definition: ActionRestriction.php:30
BlockListPager\$specialPageFactory
SpecialPageFactory $specialPageFactory
Definition: BlockListPager.php:57
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
BlockListPager\formatValue
formatValue( $name, $value)
Definition: BlockListPager.php:128
MediaWiki\Block\BlockRestrictionStore
Definition: BlockRestrictionStore.php:35