114 '$wgBlockTargetMigrationStage has an invalid read stage' );
117 parent::__construct( $context, $linkRenderer );
119 $this->blockActionInfo = $blockActionInfo;
120 $this->blockRestrictionStore = $blockRestrictionStore;
121 $this->blockUtils = $blockUtils;
122 $this->hideUserUtils = $hideUserUtils;
123 $this->commentStore = $commentStore;
124 $this->linkBatchFactory = $linkBatchFactory;
125 $this->rowCommentFormatter = $rowCommentFormatter;
126 $this->specialPageFactory = $specialPageFactory;
159 if ( $msg ===
null ) {
162 'createaccountblock',
165 'blocklist-nousertalk',
169 'blocklist-editing-sitewide',
170 'blocklist-hidden-param',
173 foreach ( $keys as $key ) {
174 $msg[$key] = $this->
msg( $key )->text();
177 '@phan-var string[] $msg';
190 $formatted = htmlspecialchars( $language->userTimeAndDate( $value, $this->getUser() ) );
194 $formatted = $this->formatTarget( $row );
198 $formatted = htmlspecialchars( $language->formatExpiry(
206 if ( $row->bt_auto ) {
207 $links[] = $linkRenderer->makeKnownLink(
208 $this->specialPageFactory->getTitleForAlias(
'Unblock' ),
211 [
'wpTarget' =>
"#{$row->bl_id}" ]
214 $target = $row->bt_address ?? $row->bt_user_text;
215 $links[] = $linkRenderer->makeKnownLink(
216 $this->specialPageFactory->getTitleForAlias(
"Unblock/$target" ),
219 $links[] = $linkRenderer->makeKnownLink(
220 $this->specialPageFactory->getTitleForAlias(
"Block/$target" ),
221 $msg[
'change-blocklink']
224 $formatted .=
' ' . Html::rawElement(
226 [
'class' =>
'mw-blocklist-actions' ],
227 $this->
msg(
'parentheses' )->rawParams(
228 $language->pipeList( $links ) )->escaped()
231 if ( $value !==
'infinity' ) {
233 $formatted .=
'<br />' . $this->
msg(
234 'ipb-blocklist-duration-left',
235 $language->formatDuration(
236 (
int)$timestamp->getTimestamp( TS_UNIX ) - MWTimestamp::time(),
250 $formatted = Linker::userLink( (
int)$value, $row->bl_by_text );
251 $formatted .= Linker::userToolLinks( (
int)$value, $row->bl_by_text );
261 if ( $row->bl_deleted ) {
262 $properties[] = htmlspecialchars( $msg[
'blocklist-hidden-param' ] );
264 if ( $row->bl_sitewide ) {
265 $properties[] = htmlspecialchars( $msg[
'blocklist-editing-sitewide'] );
268 if ( !$row->bl_sitewide && $this->restrictions ) {
269 $list = $this->getRestrictionListHTML( $row );
271 $properties[] = htmlspecialchars( $msg[
'blocklist-editing'] ) . $list;
275 if ( $row->bl_anon_only ) {
276 $properties[] = htmlspecialchars( $msg[
'anononlyblock'] );
278 if ( $row->bl_create_account ) {
279 $properties[] = htmlspecialchars( $msg[
'createaccountblock'] );
281 if ( $row->bt_user && !$row->bl_enable_autoblock ) {
282 $properties[] = htmlspecialchars( $msg[
'noautoblockblock'] );
285 if ( $row->bl_block_email ) {
286 $properties[] = htmlspecialchars( $msg[
'emailblock'] );
289 if ( !$row->bl_allow_usertalk ) {
290 $properties[] = htmlspecialchars( $msg[
'blocklist-nousertalk'] );
293 $formatted = Html::rawElement(
296 implode(
'', array_map(
static function ( $prop ) {
297 return Html::rawElement(
307 $formatted =
"Unable to format $name";
445 $commentQuery = $this->commentStore->getJoin(
'ipb_reason' );
447 'tables' => array_merge(
448 [
'ipblocks',
'ipblocks_by_actor' =>
'actor' ],
449 $commentQuery[
'tables']
452 'bt_address' =>
'ipb_address',
453 'bt_user_text' =>
'ipb_address',
454 'bt_user' =>
'ipb_user',
455 'bt_auto' =>
'ipb_auto',
456 'bt_range_start' =>
'ipb_range_start',
457 'bt_range_end' =>
'ipb_range_end',
459 'bl_by' =>
'ipblocks_by_actor.actor_user',
460 'bl_by_text' =>
'ipblocks_by_actor.actor_name',
461 'bl_timestamp' =>
'ipb_timestamp',
462 'bl_anon_only' =>
'ipb_anon_only',
463 'bl_create_account' =>
'ipb_create_account',
464 'bl_enable_autoblock' =>
'ipb_enable_autoblock',
465 'bl_expiry' =>
'ipb_expiry',
466 'bl_deleted' =>
'ipb_deleted',
467 'bl_block_email' =>
'ipb_block_email',
468 'bl_allow_usertalk' =>
'ipb_allow_usertalk',
469 'bl_sitewide' =>
'ipb_sitewide',
470 'bl_reason_text' => $commentQuery[
'fields'][
'ipb_reason_text'],
471 'bl_reason_data' => $commentQuery[
'fields'][
'ipb_reason_data'],
472 'bl_reason_cid' => $commentQuery[
'fields'][
'ipb_reason_cid'],
476 ] + $commentQuery[
'fields'],
479 'ipblocks_by_actor' => [
'JOIN',
'actor_id=ipb_by_actor' ]
480 ] + $commentQuery[
'joins']
482 # Filter out any expired blocks
483 $info[
'conds'][] = $db->expr(
'ipb_expiry',
'>', $db->timestamp() );
485 # Is the user allowed to see hidden blocks?
486 if ( !$this->
getAuthority()->isAllowed(
'hideuser' ) ) {
487 $info[
'conds'][
'ipb_deleted'] = 0;
490 $commentQuery = $this->commentStore->getJoin(
'bl_reason' );
492 'tables' => array_merge(
495 'block_by_actor' =>
'actor',
498 $commentQuery[
'tables']
510 'bl_by' =>
'block_by_actor.actor_user',
511 'bl_by_text' =>
'block_by_actor.actor_name',
515 'bl_enable_autoblock',
521 ] + $commentQuery[
'fields'],
524 'block_by_actor' => [
'JOIN',
'actor_id=bl_by_actor' ],
525 'block_target' => [
'JOIN',
'bt_id=bl_target' ],
526 ] + $commentQuery[
'joins']
529 # Filter out any expired blocks
530 $info[
'conds'][] = $db->expr(
'bl_expiry',
'>', $db->timestamp() );
532 # Filter out blocks with the deleted option if the user doesn't
533 # have permission to see hidden users
534 # TODO: consider removing this -- we could just redact them instead.
535 # The mere fact that an admin has deleted a user does not need to
536 # be private and could be included in block lists and logs for
537 # transparency purposes. Previously, filtering out deleted blocks
538 # was a convenient way to avoid showing the target name.
539 if ( !$this->
getAuthority()->isAllowed(
'hideuser' ) ) {
540 $info[
'conds'][
'bl_deleted'] = 0;
543 # Determine if the user is hidden
544 # With multiblocks we can't just rely on bl_deleted in the row being formatted
545 $info[
'fields'][
'hu_deleted'] = $this->hideUserUtils->getExpression(
546 $db,
'block_target.bt_user', HideUserUtils::HIDDEN_USERS );
577 $lb = $this->linkBatchFactory->newLinkBatch();
578 $lb->setCaller( __METHOD__ );
582 foreach ( $result as $row ) {
583 $target = $row->bt_address ?? $row->bt_user_text;
584 if ( $target !==
null ) {
589 if ( isset( $row->bl_by_text ) ) {
590 $lb->add(
NS_USER, $row->bl_by_text );
594 if ( !$row->bl_sitewide ) {
595 $partialBlocks[] = (int)$row->bl_id;
598 if ( $row->bt_user ) {
599 $userIds[] = $row->bt_user;
603 if ( $partialBlocks ) {
606 $this->restrictions = $this->blockRestrictionStore->loadByBlockId( $partialBlocks );
608 foreach ( $this->restrictions as $restriction ) {
609 if ( $restriction->getType() === PageRestriction::TYPE ) {
610 '@phan-var PageRestriction $restriction';
611 $title = $restriction->getTitle();
613 $lb->addObj( $title );
623 $this->formattedComments = $this->rowCommentFormatter->formatRows( $result,
'bl_reason' );