51 private array $formattedComments = [];
54 private array $messages = [];
57 private array $fieldNames = [];
71 protected array $conds,
76 parent::__construct( $context, $linkRenderer );
78 $this->mDefaultDirection = IndexPager::DIR_DESCENDING;
83 if ( $this->fieldNames === [] ) {
85 'bl_timestamp' =>
'blocklist-timestamp',
86 'target' =>
'blocklist-target',
87 'bl_expiry' =>
'blocklist-expiry',
88 'bl_by' =>
'blocklist-by',
89 'params' =>
'blocklist-params',
90 'bl_reason' =>
'blocklist-reason',
92 foreach ( $this->fieldNames as $key => $val ) {
93 $this->fieldNames[$key] = $this->
msg( $val )->text();
97 return $this->fieldNames;
106 if ( $this->messages === [] ) {
110 'createaccountblock',
113 'blocklist-nousertalk',
118 'blocklist-editing-sitewide',
119 'blocklist-hidden-param',
120 'blocklist-hidden-placeholder',
121 'blocklist-block-hidden',
124 foreach ( $keys as $key ) {
125 $this->messages[$key] = $this->
msg( $key )->text();
140 $formatted = $linkRenderer->makeKnownLink(
141 $this->specialPageFactory->getTitleForAlias(
'BlockList' ),
142 $language->userTimeAndDate( $value, $this->getUser() ),
144 [
'wpTarget' =>
"#{$row->bl_id}" ],
149 $formatted = $this->formatTarget( $row );
153 $formatted = htmlspecialchars( $language->formatExpiry(
160 $links = $this->getBlockChangeLinks( $row );
161 $formatted .=
' ' . Html::rawElement(
163 [
'class' =>
'mw-blocklist-actions' ],
164 $this->
msg(
'parentheses' )->rawParams(
165 $language->pipeList( $links ) )->escaped()
168 if ( $value !==
'infinity' ) {
170 $formatted .=
'<br />' . $this->
msg(
171 'ipb-blocklist-duration-left',
172 $language->formatDurationBetweenTimestamps(
173 (
int)$timestamp->getTimestamp( TS::UNIX ),
182 $formatted = Linker::userLink( (
int)$value, $row->bl_by_text );
183 $formatted .= Linker::userToolLinks( (
int)$value, $row->bl_by_text );
193 if ( intval( $row->bl_deleted ) === 1 ) {
194 $properties[] = htmlspecialchars( $this->messages[
'blocklist-hidden-param'] );
195 } elseif ( intval( $row->bl_deleted ) === 2 ) {
196 $properties[] = htmlspecialchars( $this->messages[
'blocklist-block-hidden'] );
198 if ( $row->bl_sitewide ) {
199 $properties[] = htmlspecialchars( $this->messages[
'blocklist-editing-sitewide'] );
202 if ( !$row->bl_sitewide && $this->restrictions ) {
203 $list = $this->getRestrictionListHTML( $row );
205 $properties[] = htmlspecialchars( $this->messages[
'blocklist-editing'] ) . $list;
209 if ( $row->bl_anon_only ) {
210 $properties[] = htmlspecialchars( $this->messages[
'anononlyblock'] );
212 if ( $row->bl_create_account ) {
213 $properties[] = htmlspecialchars( $this->messages[
'createaccountblock'] );
215 if ( $row->bt_user && !$row->bl_enable_autoblock ) {
216 $properties[] = htmlspecialchars( $this->messages[
'noautoblockblock'] );
219 if ( $row->bl_block_email ) {
220 $properties[] = htmlspecialchars( $this->messages[
'emailblock'] );
223 if ( !$row->bl_allow_usertalk ) {
224 $properties[] = htmlspecialchars( $this->messages[
'blocklist-nousertalk'] );
227 $formatted = Html::rawElement(
230 implode(
'', array_map(
static function ( $prop ) {
231 return Html::rawElement(
241 $formatted =
"Unable to format $name";
253 private function formatTarget( $row ) {
254 if ( $row->bt_auto ) {
255 return $this->
msg(
'autoblockid', $row->bl_id )->parse();
258 $target = $this->blockTargetFactory->newFromRowRedacted( $row );
262 $userName = $target->toString();
263 } elseif ( ( $row->hu_deleted ??
null )
264 && !$this->getAuthority()->isAllowed(
'hideuser' )
268 [
'class' =>
'mw-blocklist-hidden' ],
269 $this->messages[
'blocklist-hidden-placeholder']
271 } elseif ( $target instanceof BlockTargetWithUserPage ) {
272 $user = $target->getUserIdentity();
273 $userId = $user->getId();
274 $userName = $user->getName();
276 return $this->
msg(
'empty-username' )->escaped();
278 return Linker::userLink( $userId, $userName ) .
279 Linker::userToolLinks(
283 Linker::TOOL_LINKS_NOBLOCK
293 private function getBlockChangeLinks( $row ): array {
296 $target = $this->blockTargetFactory->newFromRowRedacted( $row )->toString();
298 $query = [
'id' => $row->bl_id ];
299 if ( $row->bt_auto ) {
300 $links[] = $linkRenderer->makeKnownLink(
301 $this->specialPageFactory->getTitleForAlias(
'Unblock' ),
302 $this->messages[
'remove-blocklink'],
304 [
'wpTarget' =>
"#{$row->bl_id}" ]
307 $specialBlock = $this->specialPageFactory->getTitleForAlias(
"Block/$target" );
308 $links[] = $linkRenderer->makeKnownLink(
310 $this->messages[
'remove-blocklink'],
312 $query + [
'remove' =>
'1' ]
314 $links[] = $linkRenderer->makeKnownLink(
316 $this->messages[
'change-blocklink'],
322 if ( $row->bt_auto ) {
323 $links[] = $linkRenderer->makeKnownLink(
324 $this->specialPageFactory->getTitleForAlias(
'Unblock' ),
325 $this->messages[
'unblocklink'],
327 [
'wpTarget' =>
"#{$row->bl_id}" ]
330 $links[] = $linkRenderer->makeKnownLink(
331 $this->specialPageFactory->getTitleForAlias(
"Unblock/$target" ),
332 $this->messages[
'unblocklink']
334 $links[] = $linkRenderer->makeKnownLink(
335 $this->specialPageFactory->getTitleForAlias(
"Block/$target" ),
336 $this->messages[
'change-blocklink']
350 private function getRestrictionListHTML( stdClass $row ) {
354 foreach ( $this->restrictions as $restriction ) {
355 if ( $restriction->getBlockId() !== (
int)$row->bl_id ) {
359 switch ( $restriction->getType() ) {
360 case PageRestriction::TYPE:
361 '@phan-var PageRestriction $restriction';
362 if ( $restriction->getTitle() ) {
363 $items[$restriction->getType()][] = Html::rawElement(
366 $linkRenderer->makeLink( $restriction->getTitle() )
370 case NamespaceRestriction::TYPE:
371 $text = $restriction->getValue() ===
NS_MAIN
372 ? $this->messages[
'blanknamespace']
374 $restriction->getValue()
377 $items[$restriction->getType()][] = Html::rawElement(
380 $linkRenderer->makeLink(
381 $this->specialPageFactory->getTitleForAlias(
'Allpages' ),
385 'namespace' => $restriction->getValue()
391 case ActionRestriction::TYPE:
392 $actionName = $this->blockActionInfo->getActionFromId( $restriction->getValue() );
401 $this->
msg(
'ipb-action-' .
402 $this->blockActionInfo->getActionFromId( $restriction->getValue() ) )->text()
414 foreach ( $items as $key => $value ) {
415 $sets[] = Html::rawElement(
423 $this->
msg(
'blocklist-editing-' . $key ) . Html::rawElement(
426 implode(
'', $value )
431 return Html::rawElement(
440 $db = $this->getDatabase();
441 $commentQuery = $this->commentStore->getJoin(
'bl_reason' );
445 'block_by_actor' =>
'actor',
446 'block_target' =>
'block_target',
447 ...$commentQuery[
'tables'],
459 'bl_by' =>
'block_by_actor.actor_user',
460 'bl_by_text' =>
'block_by_actor.actor_name',
464 'bl_enable_autoblock',
470 ] + $commentQuery[
'fields'],
471 'conds' => $this->conds,
473 'block_by_actor' => [
'JOIN',
'actor_id=bl_by_actor' ],
474 'block_target' => [
'JOIN',
'bt_id=bl_target' ],
475 ] + $commentQuery[
'joins']
478 # Filter out any expired blocks
479 $info[
'conds'][] = $db->expr(
'bl_expiry',
'>', $db->timestamp() );
481 # Filter out blocks with the deleted option if the user doesn't
482 # have permission to see hidden users
483 # TODO: consider removing this -- we could just redact them instead.
484 # The mere fact that an admin has deleted a user does not need to
485 # be private and could be included in block lists and logs for
486 # transparency purposes. Previously, filtering out deleted blocks
487 # was a convenient way to avoid showing the target name.
488 if ( $this->getAuthority()->isAllowed(
'hideuser' ) ) {
489 $info[
'fields'][
'hu_deleted'] = $this->hideUserUtils->getExpression(
491 'block_target.bt_user',
492 HideUserUtils::HIDDEN_USERS
495 $info[
'fields'][
'hu_deleted'] = 0;
496 $info[
'conds'][] = $this->hideUserUtils->getExpression(
498 'block_target.bt_user',
499 HideUserUtils::SHOWN_USERS
501 $info[
'conds'][
'bl_deleted'] = 0;
508 return parent::getTableClass() .
' mw-blocklist';
513 return [ [
'bl_timestamp',
'bl_id' ] ];
532 $lb = $this->linkBatchFactory->newLinkBatch();
533 $lb->setCaller( __METHOD__ );
536 foreach ( $result as $row ) {
537 $target = $row->bt_address ?? $row->bt_user_text;
538 if ( $target !==
null ) {
542 if ( isset( $row->bl_by_text ) ) {
543 $lb->add(
NS_USER, $row->bl_by_text );
547 if ( !$row->bl_sitewide ) {
548 $partialBlocks[] = (int)$row->bl_id;
552 if ( $partialBlocks ) {
555 $this->restrictions = $this->blockRestrictionStore->loadByBlockId( $partialBlocks );
557 foreach ( $this->restrictions as $restriction ) {
558 if ( $restriction->getType() === PageRestriction::TYPE ) {
559 '@phan-var PageRestriction $restriction';
560 $title = $restriction->getTitle();
562 $lb->addObj( $title );
572 $this->formattedComments = $this->rowCommentFormatter->formatRows( $result,
'bl_reason' );