MediaWiki master
SpecialBlockList.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Specials;
8
9use InvalidArgumentException;
24use MediaWiki\Pager\BlockListPager;
31
41 protected $target;
42
44 protected $options;
45
47 protected $blockType;
48
49 public function __construct(
50 private readonly LinkBatchFactory $linkBatchFactory,
51 private readonly DatabaseBlockStore $blockStore,
52 private readonly BlockRestrictionStore $blockRestrictionStore,
53 private readonly IConnectionProvider $dbProvider,
54 private readonly CommentStore $commentStore,
55 private readonly BlockTargetFactory $blockTargetFactory,
56 private readonly HideUserUtils $hideUserUtils,
57 private readonly BlockActionInfo $blockActionInfo,
58 private readonly RowCommentFormatter $rowCommentFormatter,
59 private readonly TempUserConfig $tempUserConfig
60 ) {
61 parent::__construct( 'BlockList' );
62 }
63
67 public function execute( $par ) {
68 $this->setHeaders();
69 $this->outputHeader();
70 $this->addHelpLink( 'Help:Blocking_users' );
71 $out = $this->getOutput();
72 $out->setPageTitleMsg( $this->msg( 'ipblocklist' ) );
73 $out->addModuleStyles( [ 'mediawiki.special' ] );
74
75 $request = $this->getRequest();
76 $par = $request->getVal( 'ip', $par ?? '' );
77 $this->target = trim( $request->getVal( 'wpTarget', $par ) );
78
79 $this->options = $request->getArray( 'wpOptions', [] );
80 $this->blockType = $request->getVal( 'blockType' );
81
82 $action = $request->getText( 'action' );
83
84 if ( $action == 'unblock' || ( $action == 'submit' && $request->wasPosted() ) ) {
85 // B/C @since 1.18: Unblock interface is now at Special:Unblock
86 $title = $this->getSpecialPageFactory()->getTitleForAlias( 'Unblock/' . $this->target );
87 $out->redirect( $title->getFullURL() );
88
89 return;
90 }
91
92 // Setup BlockListPager here to get the actual default Limit
93 $pager = $this->getBlockListPager();
94
95 $blockFilterOptions = [
96 'blocklist-tempblocks' => 'tempblocks',
97 'blocklist-indefblocks' => 'indefblocks',
98 'blocklist-autoblocks' => 'autoblocks',
99 'blocklist-addressblocks' => 'addressblocks',
100 'blocklist-rangeblocks' => 'rangeblocks',
101 ];
102
103 if ( $this->tempUserConfig->isKnown() ) {
104 // Clarify that "userblocks" excludes named users only if temporary accounts are known (T380266)
105 $blockFilterOptions['blocklist-nameduserblocks'] = 'userblocks';
106 $blockFilterOptions['blocklist-tempuserblocks'] = 'tempuserblocks';
107 } else {
108 $blockFilterOptions['blocklist-userblocks'] = 'userblocks';
109 }
110
111 // Just show the block list
112 $fields = [
113 'Target' => [
114 'type' => 'user',
115 'label-message' => 'ipaddressorusername',
116 'tabindex' => '1',
117 'size' => '45',
118 'default' => $this->target,
119 ],
120 'Options' => [
121 'type' => 'multiselect',
122 'options-messages' => $blockFilterOptions,
123 'flatlist' => true,
124 ],
125 ];
126
127 $fields['BlockType'] = [
128 'type' => 'select',
129 'label-message' => 'blocklist-type',
130 'options' => [
131 $this->msg( 'blocklist-type-opt-all' )->escaped() => '',
132 $this->msg( 'blocklist-type-opt-sitewide' )->escaped() => 'sitewide',
133 $this->msg( 'blocklist-type-opt-partial' )->escaped() => 'partial',
134 ],
135 'name' => 'blockType',
136 'cssclass' => 'mw-field-block-type',
137 ];
138
139 $fields['Limit'] = [
140 'type' => 'limitselect',
141 'label-message' => 'table_pager_limit_label',
142 'options' => $pager->getLimitSelectList(),
143 'name' => 'limit',
144 'default' => $pager->getLimit(),
145 'cssclass' => 'mw-field-limit mw-has-field-block-type',
146 ];
147
148 $form = HTMLForm::factory( 'ooui', $fields, $this->getContext() );
149 $form
150 ->setMethod( 'get' )
151 ->setTitle( $this->getPageTitle() ) // Remove subpage
152 ->setFormIdentifier( 'blocklist' )
153 ->setWrapperLegendMsg( 'ipblocklist-legend' )
154 ->setSubmitTextMsg( 'ipblocklist-submit' )
155 ->prepareForm()
156 ->displayForm( false );
157
158 $this->showList( $pager );
159 }
160
165 protected function getBlockListPager() {
166 $conds = [];
167 $db = $this->getDB();
168
169 // Add target conditions
170 if ( $this->target !== '' ) {
171 $target = $this->blockTargetFactory->newFromString( $this->target );
172 if ( $target ) {
173 $conds = $this->getTargetConds( $target );
174 }
175 }
176
177 // Apply filters
178 if ( in_array( 'userblocks', $this->options ) ) {
179 $namedUserConds = $db->expr( 'bt_user', '=', null );
180
181 // If temporary accounts are a known concept on this wiki,
182 // have the "Hide account blocks" filter exclude only named users (T380266).
183 if ( $this->tempUserConfig->isKnown() ) {
184 $namedUserConds = $namedUserConds->orExpr(
185 $this->tempUserConfig->getMatchCondition( $db, 'bt_user_text', IExpression::LIKE )
186 );
187 }
188
189 $conds[] = $namedUserConds;
190 }
191 if ( in_array( 'autoblocks', $this->options ) ) {
192 $conds['bl_parent_block_id'] = null;
193 }
194 if ( in_array( 'addressblocks', $this->options )
195 && in_array( 'rangeblocks', $this->options )
196 ) {
197 // Simpler conditions for only user blocks (T360864)
198 $conds[] = $db->expr( 'bt_user', '!=', null );
199 } elseif ( in_array( 'addressblocks', $this->options ) ) {
200 $conds[] = $db->expr( 'bt_user', '!=', null )->or( 'bt_range_start', '!=', null );
201 } elseif ( in_array( 'rangeblocks', $this->options ) ) {
202 $conds['bt_range_start'] = null;
203 }
204
205 if (
206 in_array( 'tempuserblocks', $this->options ) &&
207 $this->tempUserConfig->isKnown()
208 ) {
209 $conds[] = $db->expr( 'bt_user', '=', null )
210 ->orExpr(
211 $this->tempUserConfig->getMatchCondition( $db, 'bt_user_text', IExpression::NOT_LIKE )
212 );
213 }
214
215 $hideTemp = in_array( 'tempblocks', $this->options );
216 $hideIndef = in_array( 'indefblocks', $this->options );
217 if ( $hideTemp && $hideIndef ) {
218 // If both types are hidden, ensure query doesn't produce any results
219 $conds[] = '1=0';
220 } elseif ( $hideTemp ) {
221 $conds['bl_expiry'] = $db->getInfinity();
222 } elseif ( $hideIndef ) {
223 $conds[] = $db->expr( 'bl_expiry', '!=', $db->getInfinity() );
224 }
225
226 if ( $this->blockType === 'sitewide' ) {
227 $conds['bl_sitewide'] = 1;
228 } elseif ( $this->blockType === 'partial' ) {
229 $conds['bl_sitewide'] = 0;
230 }
231
232 return new BlockListPager(
233 $this->getContext(),
234 $this->blockActionInfo,
235 $this->blockRestrictionStore,
236 $this->blockTargetFactory,
237 $this->hideUserUtils,
238 $this->commentStore,
239 $this->linkBatchFactory,
240 $this->getLinkRenderer(),
241 $this->dbProvider,
242 $this->rowCommentFormatter,
243 $this->getSpecialPageFactory(),
244 $conds
245 );
246 }
247
261 private function getTargetConds( BlockTarget $target ) {
262 if ( $target instanceof AutoBlockTarget ) {
263 return [ 'bl_id' => $target->getId() ];
264 }
265 if ( $target instanceof BlockTargetWithIp ) {
266 $range = $target->toHexRange();
267 return [
268 $this->blockStore->getRangeCond( $range[0], $range[1] ),
269 'bt_auto' => 0
270 ];
271 }
272 if ( $target instanceof UserBlockTarget ) {
273 $user = $target->getUserIdentity();
274 if ( $user->getId() ) {
275 return [
276 'bt_user' => $user->getId(),
277 'bt_auto' => 0
278 ];
279 } else {
280 // No such user
281 return [ '1=0' ];
282 }
283 }
284 throw new InvalidArgumentException( 'Invalid block target type' );
285 }
286
291 protected function showList( BlockListPager $pager ) {
292 $out = $this->getOutput();
293
294 // Check for other blocks, i.e. global/tor blocks
295 $otherBlockLink = [];
296 $this->getHookRunner()->onOtherBlockLogLink( $otherBlockLink, $this->target );
297
298 // Show additional header for the local block only when other blocks exists.
299 // Not necessary in a standard installation without such extensions enabled
300 if ( count( $otherBlockLink ) ) {
301 $out->addHTML(
302 Html::element( 'h2', [], $this->msg( 'ipblocklist-localblock' )->text() ) . "\n"
303 );
304 }
305
306 if ( $pager->getNumRows() ) {
307 $out->addParserOutputContent(
308 $pager->getFullOutput(),
309 ParserOptions::newFromContext( $this->getContext() )
310 );
311 } elseif ( $this->target ) {
312 $out->addWikiMsg( 'ipblocklist-no-results' );
313 } else {
314 $out->addWikiMsg( 'ipblocklist-empty' );
315 }
316
317 if ( count( $otherBlockLink ) ) {
318 $out->addHTML(
319 Html::rawElement(
320 'h2',
321 [],
322 $this->msg( 'ipblocklist-otherblocks', count( $otherBlockLink ) )->parse()
323 ) . "\n"
324 );
325 $list = '';
326 foreach ( $otherBlockLink as $link ) {
327 $list .= Html::rawElement( 'li', [], $link ) . "\n";
328 }
329 $out->addHTML( Html::rawElement(
330 'ul',
331 [ 'class' => 'mw-ipblocklist-otherblocks' ],
332 $list
333 ) . "\n" );
334 }
335 }
336
338 protected function getGroupName() {
339 return 'users';
340 }
341
347 protected function getDB() {
348 return $this->dbProvider->getReplicaDatabase();
349 }
350}
351
353class_alias( SpecialBlockList::class, 'SpecialBlockList' );
A block target of the form #1234 where the number is the block ID.
Defines the actions that can be blocked by a partial block.
Factory for BlockTarget objects.
Base class for block targets.
Helpers for building queries that determine whether a user is hidden.
A block target for a registered user.
This is basically a CommentFormatter with a CommentStore dependency, allowing it to retrieve comment ...
Handle database storage of comments such as edit summaries and log reasons.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:207
This class is a collection of static functions that serve two purposes:
Definition Html.php:43
Factory for LinkBatch objects to batch query page metadata.
Set options of the Parser.
Parent class for all special pages.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getPageTitle( $subpage=false)
Get a self-referential title object.
getContext()
Gets the context this SpecialPage is executed in.
getRequest()
Get the WebRequest being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages By default the message key is the canonical name of...
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
showList(BlockListPager $pager)
Show the list of blocked accounts matching the actual filter.
__construct(private readonly LinkBatchFactory $linkBatchFactory, private readonly DatabaseBlockStore $blockStore, private readonly BlockRestrictionStore $blockRestrictionStore, private readonly IConnectionProvider $dbProvider, private readonly CommentStore $commentStore, private readonly BlockTargetFactory $blockTargetFactory, private readonly HideUserUtils $hideUserUtils, private readonly BlockActionInfo $blockActionInfo, private readonly RowCommentFormatter $rowCommentFormatter, private readonly TempUserConfig $tempUserConfig)
getDB()
Return a IDatabase object for reading.
getBlockListPager()
Setup a new BlockListPager instance.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
Shared interface for IP or range blocks.
Interface for temporary user creation config and name matching.
Provide primary and replica IDatabase connections.
A database connection without write operations.
element(SerializerNode $parent, SerializerNode $node, $contents)