MediaWiki  master
BlockListPager.php
Go to the documentation of this file.
1 <?php
33 use Wikimedia\IPUtils;
36 
40 class BlockListPager extends TablePager {
41 
42  protected $conds;
43 
49  protected $restrictions = [];
50 
53 
56 
59 
61  private $commentStore;
62 
64  private $blockUtils;
65 
68 
71 
73  private $formattedComments = [];
74 
87  public function __construct(
88  $page,
89  $conds,
92  ILoadBalancer $loadBalancer,
98  ) {
99  $this->mDb = $loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
100  parent::__construct( $page->getContext(), $page->getLinkRenderer() );
101  $this->conds = $conds;
102  $this->mDefaultDirection = IndexPager::DIR_DESCENDING;
103  $this->linkBatchFactory = $linkBatchFactory;
104  $this->blockRestrictionStore = $blockRestrictionStore;
105  $this->specialPageFactory = $specialPageFactory;
106  $this->commentStore = $commentStore;
107  $this->blockUtils = $blockUtils;
108  $this->blockActionInfo = $blockActionInfo;
109  $this->rowCommentFormatter = $rowCommentFormatter;
110  }
111 
112  protected function getFieldNames() {
113  static $headers = null;
114 
115  if ( $headers === null ) {
116  $headers = [
117  'ipb_timestamp' => 'blocklist-timestamp',
118  'ipb_target' => 'blocklist-target',
119  'ipb_expiry' => 'blocklist-expiry',
120  'ipb_by' => 'blocklist-by',
121  'ipb_params' => 'blocklist-params',
122  'ipb_reason' => 'blocklist-reason',
123  ];
124  foreach ( $headers as $key => $val ) {
125  $headers[$key] = $this->msg( $val )->text();
126  }
127  }
128 
129  return $headers;
130  }
131 
138  public function formatValue( $name, $value ) {
139  static $msg = null;
140  if ( $msg === null ) {
141  $keys = [
142  'anononlyblock',
143  'createaccountblock',
144  'noautoblockblock',
145  'emailblock',
146  'blocklist-nousertalk',
147  'unblocklink',
148  'change-blocklink',
149  'blocklist-editing',
150  'blocklist-editing-sitewide',
151  ];
152 
153  foreach ( $keys as $key ) {
154  $msg[$key] = $this->msg( $key )->text();
155  }
156  }
157  '@phan-var string[] $msg';
158 
160  $row = $this->mCurrentRow;
161 
162  $language = $this->getLanguage();
163 
164  $formatted = '';
165 
166  $linkRenderer = $this->getLinkRenderer();
167 
168  switch ( $name ) {
169  case 'ipb_timestamp':
170  $formatted = htmlspecialchars( $language->userTimeAndDate( $value, $this->getUser() ) );
171  break;
172 
173  case 'ipb_target':
174  if ( $row->ipb_auto ) {
175  $formatted = $this->msg( 'autoblockid', $row->ipb_id )->parse();
176  } else {
177  list( $target, ) = $this->blockUtils->parseBlockTarget( $row->ipb_address );
178 
179  if ( is_string( $target ) ) {
180  if ( IPUtils::isValidRange( $target ) ) {
181  $target = User::newFromName( $target, false );
182  } else {
183  $formatted = $target;
184  }
185  }
186 
187  if ( $target instanceof UserIdentity ) {
188  $formatted = Linker::userLink( $target->getId(), $target->getName() );
189  $formatted .= Linker::userToolLinks(
190  $target->getId(),
191  $target->getName(),
192  false,
194  );
195  }
196  }
197  break;
198 
199  case 'ipb_expiry':
200  $formatted = htmlspecialchars( $language->formatExpiry(
201  $value,
202  /* User preference timezone */true,
203  'infinity',
204  $this->getUser()
205  ) );
206  if ( $this->getAuthority()->isAllowed( 'block' ) ) {
207  $links = [];
208  if ( $row->ipb_auto ) {
209  $links[] = $linkRenderer->makeKnownLink(
210  $this->specialPageFactory->getTitleForAlias( 'Unblock' ),
211  $msg['unblocklink'],
212  [],
213  [ 'wpTarget' => "#{$row->ipb_id}" ]
214  );
215  } else {
216  $links[] = $linkRenderer->makeKnownLink(
217  $this->specialPageFactory->getTitleForAlias( 'Unblock/' . $row->ipb_address ),
218  $msg['unblocklink']
219  );
220  $links[] = $linkRenderer->makeKnownLink(
221  $this->specialPageFactory->getTitleForAlias( 'Block/' . $row->ipb_address ),
222  $msg['change-blocklink']
223  );
224  }
225  $formatted .= ' ' . Html::rawElement(
226  'span',
227  [ 'class' => 'mw-blocklist-actions' ],
228  $this->msg( 'parentheses' )->rawParams(
229  $language->pipeList( $links ) )->escaped()
230  );
231  }
232  if ( $value !== 'infinity' ) {
233  $timestamp = new MWTimestamp( $value );
234  $formatted .= '<br />' . $this->msg(
235  'ipb-blocklist-duration-left',
236  $language->formatDuration(
237  $timestamp->getTimestamp() - MWTimestamp::time(),
238  // reasonable output
239  [
240  'minutes',
241  'hours',
242  'days',
243  'years',
244  ]
245  )
246  )->escaped();
247  }
248  break;
249 
250  case 'ipb_by':
251  $formatted = Linker::userLink( $value, $row->ipb_by_text );
252  $formatted .= Linker::userToolLinks( $value, $row->ipb_by_text );
253  break;
254 
255  case 'ipb_reason':
256  $formatted = $this->formattedComments[$this->getResultOffset()];
257  break;
258 
259  case 'ipb_params':
260  $properties = [];
261 
262  if ( $row->ipb_sitewide ) {
263  $properties[] = htmlspecialchars( $msg['blocklist-editing-sitewide'] );
264  }
265 
266  if ( !$row->ipb_sitewide && $this->restrictions ) {
267  $list = $this->getRestrictionListHTML( $row );
268  if ( $list ) {
269  $properties[] = htmlspecialchars( $msg['blocklist-editing'] ) . $list;
270  }
271  }
272 
273  if ( $row->ipb_anon_only ) {
274  $properties[] = htmlspecialchars( $msg['anononlyblock'] );
275  }
276  if ( $row->ipb_create_account ) {
277  $properties[] = htmlspecialchars( $msg['createaccountblock'] );
278  }
279  if ( $row->ipb_user && !$row->ipb_enable_autoblock ) {
280  $properties[] = htmlspecialchars( $msg['noautoblockblock'] );
281  }
282 
283  if ( $row->ipb_block_email ) {
284  $properties[] = htmlspecialchars( $msg['emailblock'] );
285  }
286 
287  if ( !$row->ipb_allow_usertalk ) {
288  $properties[] = htmlspecialchars( $msg['blocklist-nousertalk'] );
289  }
290 
291  $formatted = Html::rawElement(
292  'ul',
293  [],
294  implode( '', array_map( static function ( $prop ) {
295  return Html::rawElement(
296  'li',
297  [],
298  $prop
299  );
300  }, $properties ) )
301  );
302  break;
303 
304  default:
305  $formatted = "Unable to format $name";
306  break;
307  }
308 
309  return $formatted;
310  }
311 
319  private function getRestrictionListHTML( stdClass $row ) {
320  $items = [];
321  $linkRenderer = $this->getLinkRenderer();
322 
323  foreach ( $this->restrictions as $restriction ) {
324  if ( $restriction->getBlockId() !== (int)$row->ipb_id ) {
325  continue;
326  }
327 
328  switch ( $restriction->getType() ) {
329  case PageRestriction::TYPE:
330  '@phan-var PageRestriction $restriction';
331  if ( $restriction->getTitle() ) {
332  $items[$restriction->getType()][] = Html::rawElement(
333  'li',
334  [],
335  $linkRenderer->makeLink( $restriction->getTitle() )
336  );
337  }
338  break;
339  case NamespaceRestriction::TYPE:
340  $text = $restriction->getValue() === NS_MAIN
341  ? $this->msg( 'blanknamespace' )->text()
342  : $this->getLanguage()->getFormattedNsText(
343  $restriction->getValue()
344  );
345  if ( $text ) {
346  $items[$restriction->getType()][] = Html::rawElement(
347  'li',
348  [],
349  $linkRenderer->makeLink(
350  $this->specialPageFactory->getTitleForAlias( 'Allpages' ),
351  $text,
352  [],
353  [
354  'namespace' => $restriction->getValue()
355  ]
356  )
357  );
358  }
359  break;
360  case ActionRestriction::TYPE:
361  $actionName = $this->blockActionInfo->getActionFromId( $restriction->getValue() );
362  $enablePartialActionBlocks = $this->getConfig()->get( 'EnablePartialActionBlocks' );
363  if ( $actionName && $enablePartialActionBlocks ) {
364  $items[$restriction->getType()][] = Html::rawElement(
365  'li',
366  [],
367  $this->msg( 'ipb-action-' .
368  $this->blockActionInfo->getActionFromId( $restriction->getValue() ) )->escaped()
369  );
370  }
371  break;
372  }
373  }
374 
375  if ( empty( $items ) ) {
376  return '';
377  }
378 
379  $sets = [];
380  foreach ( $items as $key => $value ) {
381  $sets[] = Html::rawElement(
382  'li',
383  [],
384  $this->msg( 'blocklist-editing-' . $key ) . Html::rawElement(
385  'ul',
386  [],
387  implode( '', $value )
388  )
389  );
390  }
391 
392  return Html::rawElement(
393  'ul',
394  [],
395  implode( '', $sets )
396  );
397  }
398 
399  public function getQueryInfo() {
400  $commentQuery = $this->commentStore->getJoin( 'ipb_reason' );
401 
402  $info = [
403  'tables' => array_merge(
404  [ 'ipblocks', 'ipblocks_by_actor' => 'actor' ],
405  $commentQuery['tables']
406  ),
407  'fields' => [
408  'ipb_id',
409  'ipb_address',
410  'ipb_user',
411  'ipb_by' => 'ipblocks_by_actor.actor_user',
412  'ipb_by_text' => 'ipblocks_by_actor.actor_name',
413  'ipb_timestamp',
414  'ipb_auto',
415  'ipb_anon_only',
416  'ipb_create_account',
417  'ipb_enable_autoblock',
418  'ipb_expiry',
419  'ipb_range_start',
420  'ipb_range_end',
421  'ipb_deleted',
422  'ipb_block_email',
423  'ipb_allow_usertalk',
424  'ipb_sitewide',
425  ] + $commentQuery['fields'],
426  'conds' => $this->conds,
427  'join_conds' => [
428  'ipblocks_by_actor' => [ 'JOIN', 'actor_id=ipb_by_actor' ]
429  ] + $commentQuery['joins']
430  ];
431 
432  # Filter out any expired blocks
433  $db = $this->getDatabase();
434  $info['conds'][] = 'ipb_expiry > ' . $db->addQuotes( $db->timestamp() );
435 
436  # Is the user allowed to see hidden blocks?
437  if ( !$this->getAuthority()->isAllowed( 'hideuser' ) ) {
438  $info['conds']['ipb_deleted'] = 0;
439  }
440 
441  return $info;
442  }
443 
449  public function getTotalAutoblocks() {
450  $dbr = $this->getDatabase();
451  return (int)$dbr->selectField( 'ipblocks', 'COUNT(*)',
452  [
453  'ipb_auto' => '1',
454  'ipb_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() ),
455  ],
456  __METHOD__
457  );
458  }
459 
460  protected function getTableClass() {
461  return parent::getTableClass() . ' mw-blocklist';
462  }
463 
464  public function getIndexField() {
465  return [ [ 'ipb_timestamp', 'ipb_id' ] ];
466  }
467 
468  public function getDefaultSort() {
469  return '';
470  }
471 
472  protected function isFieldSortable( $name ) {
473  return false;
474  }
475 
480  public function preprocessResults( $result ) {
481  // Do a link batch query
482  $lb = $this->linkBatchFactory->newLinkBatch();
483  $lb->setCaller( __METHOD__ );
484 
485  $partialBlocks = [];
486  foreach ( $result as $row ) {
487  $lb->add( NS_USER, $row->ipb_address );
488  $lb->add( NS_USER_TALK, $row->ipb_address );
489 
490  if ( $row->ipb_by ?? null ) {
491  $lb->add( NS_USER, $row->ipb_by_text );
492  $lb->add( NS_USER_TALK, $row->ipb_by_text );
493  }
494 
495  if ( !$row->ipb_sitewide ) {
496  $partialBlocks[] = $row->ipb_id;
497  }
498  }
499 
500  if ( $partialBlocks ) {
501  // Mutations to the $row object are not persisted. The restrictions will
502  // need be stored in a separate store.
503  $this->restrictions = $this->blockRestrictionStore->loadByBlockId( $partialBlocks );
504 
505  foreach ( $this->restrictions as $restriction ) {
506  if ( $restriction->getType() === PageRestriction::TYPE ) {
507  '@phan-var PageRestriction $restriction';
508  $title = $restriction->getTitle();
509  if ( $title ) {
510  $lb->addObj( $title );
511  }
512  }
513  }
514  }
515 
516  $lb->execute();
517 
518  // Format comments
519  // The keys of formattedComments will be the corresponding offset into $result
520  $this->formattedComments = $this->rowCommentFormatter->formatRows( $result, 'ipb_reason' );
521  }
522 
523 }
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:42
BlockListPager\isFieldSortable
isFieldSortable( $name)
Return true if the named field should be sortable by the UI, false otherwise.
Definition: BlockListPager.php:472
Linker\userLink
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:1064
BlockListPager
Definition: BlockListPager.php:40
BlockListPager\__construct
__construct( $page, $conds, LinkBatchFactory $linkBatchFactory, BlockRestrictionStore $blockRestrictionStore, ILoadBalancer $loadBalancer, SpecialPageFactory $specialPageFactory, CommentStore $commentStore, BlockUtils $blockUtils, BlockActionInfo $blockActionInfo, RowCommentFormatter $rowCommentFormatter)
Definition: BlockListPager.php:87
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:1109
BlockListPager\$blockRestrictionStore
BlockRestrictionStore $blockRestrictionStore
Definition: BlockListPager.php:55
IndexPager\getDatabase
getDatabase()
Get the Database object in use.
Definition: IndexPager.php:248
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:319
User\newFromName
static newFromName( $name, $validate='valid')
Definition: User.php:606
BlockListPager\$conds
$conds
Definition: BlockListPager.php:42
IndexPager\$linkRenderer
LinkRenderer $linkRenderer
Definition: IndexPager.php:167
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:67
BlockListPager\$linkBatchFactory
LinkBatchFactory $linkBatchFactory
Definition: BlockListPager.php:52
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:64
BlockListPager\$commentStore
CommentStore $commentStore
Definition: BlockListPager.php:61
Wikimedia\Rdbms\IResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: IResultWrapper.php:26
MediaWiki\Cache\LinkBatchFactory
Definition: LinkBatchFactory.php:39
BlockListPager\getTableClass
getTableClass()
TablePager relies on mw-datatable for styling, see T214208.
Definition: BlockListPager.php:460
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:112
BlockListPager\$restrictions
Restriction[] $restrictions
Array of restrictions.
Definition: BlockListPager.php:49
BlockListPager\getDefaultSort
getDefaultSort()
The database field name used as a default sort order.
Definition: BlockListPager.php:468
$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:464
ContextSource\getAuthority
getAuthority()
Definition: ContextSource.php:144
BlockListPager\$formattedComments
string[] $formattedComments
Definition: BlockListPager.php:73
BlockListPager\getQueryInfo
getQueryInfo()
Provides all parameters needed for the main paged query.
Definition: BlockListPager.php:399
BlockListPager\getTotalAutoblocks
getTotalAutoblocks()
Get total number of autoblocks at any given time.
Definition: BlockListPager.php:449
MediaWiki\Block\BlockActionInfo
Defines the actions that can be blocked by a partial block.
Definition: BlockActionInfo.php:40
IndexPager\getLinkRenderer
getLinkRenderer()
Definition: IndexPager.php:991
NS_USER
const NS_USER
Definition: Defines.php:66
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\CommentFormatter\RowCommentFormatter
This is basically a CommentFormatter with a CommentStore dependency, allowing it to retrieve comment ...
Definition: RowCommentFormatter.php:15
MediaWiki\Block\Restriction\PageRestriction
Definition: PageRestriction.php:25
BlockListPager\$rowCommentFormatter
RowCommentFormatter $rowCommentFormatter
Definition: BlockListPager.php:70
BlockListPager\preprocessResults
preprocessResults( $result)
Do a LinkBatch query to minimise database load when generating all these links.
Definition: BlockListPager.php:480
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:210
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:58
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:138
MediaWiki\Block\BlockRestrictionStore
Definition: BlockRestrictionStore.php:35
IndexPager\getResultOffset
getResultOffset()
Definition: IndexPager.php:314