MediaWiki master
SpecialBlockList.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Specials;
22
23use InvalidArgumentException;
45
55 protected $target;
56
58 protected $options;
59
61 protected $blockType;
62
63 private LinkBatchFactory $linkBatchFactory;
64 private DatabaseBlockStore $blockStore;
65 private BlockRestrictionStore $blockRestrictionStore;
66 private IConnectionProvider $dbProvider;
67 private CommentStore $commentStore;
68 private BlockTargetFactory $blockTargetFactory;
69 private HideUserUtils $hideUserUtils;
70 private BlockActionInfo $blockActionInfo;
71 private RowCommentFormatter $rowCommentFormatter;
72 private TempUserConfig $tempUserConfig;
73
74 public function __construct(
75 LinkBatchFactory $linkBatchFactory,
76 DatabaseBlockStore $blockStore,
77 BlockRestrictionStore $blockRestrictionStore,
78 IConnectionProvider $dbProvider,
79 CommentStore $commentStore,
80 BlockTargetFactory $blockTargetFactory,
81 HideUserUtils $hideUserUtils,
82 BlockActionInfo $blockActionInfo,
83 RowCommentFormatter $rowCommentFormatter,
84 TempUserConfig $tempUserConfig
85 ) {
86 parent::__construct( 'BlockList' );
87
88 $this->linkBatchFactory = $linkBatchFactory;
89 $this->blockStore = $blockStore;
90 $this->blockRestrictionStore = $blockRestrictionStore;
91 $this->dbProvider = $dbProvider;
92 $this->commentStore = $commentStore;
93 $this->blockTargetFactory = $blockTargetFactory;
94 $this->hideUserUtils = $hideUserUtils;
95 $this->blockActionInfo = $blockActionInfo;
96 $this->rowCommentFormatter = $rowCommentFormatter;
97 $this->tempUserConfig = $tempUserConfig;
98 }
99
103 public function execute( $par ) {
104 $this->setHeaders();
105 $this->outputHeader();
106 $this->addHelpLink( 'Help:Blocking_users' );
107 $out = $this->getOutput();
108 $out->setPageTitleMsg( $this->msg( 'ipblocklist' ) );
109 $out->addModuleStyles( [ 'mediawiki.special' ] );
110
111 $request = $this->getRequest();
112 $par = $request->getVal( 'ip', $par ?? '' );
113 $this->target = trim( $request->getVal( 'wpTarget', $par ) );
114
115 $this->options = $request->getArray( 'wpOptions', [] );
116 $this->blockType = $request->getVal( 'blockType' );
117
118 $action = $request->getText( 'action' );
119
120 if ( $action == 'unblock' || ( $action == 'submit' && $request->wasPosted() ) ) {
121 // B/C @since 1.18: Unblock interface is now at Special:Unblock
122 $title = $this->getSpecialPageFactory()->getTitleForAlias( 'Unblock/' . $this->target );
123 $out->redirect( $title->getFullURL() );
124
125 return;
126 }
127
128 // Setup BlockListPager here to get the actual default Limit
129 $pager = $this->getBlockListPager();
130
131 $blockFilterOptions = [
132 'blocklist-tempblocks' => 'tempblocks',
133 'blocklist-indefblocks' => 'indefblocks',
134 'blocklist-autoblocks' => 'autoblocks',
135 'blocklist-addressblocks' => 'addressblocks',
136 'blocklist-rangeblocks' => 'rangeblocks',
137 ];
138
139 if ( $this->tempUserConfig->isKnown() ) {
140 // Clarify that "userblocks" excludes named users only if temporary accounts are known (T380266)
141 $blockFilterOptions['blocklist-nameduserblocks'] = 'userblocks';
142 $blockFilterOptions['blocklist-tempuserblocks'] = 'tempuserblocks';
143 } else {
144 $blockFilterOptions['blocklist-userblocks'] = 'userblocks';
145 }
146
147 // Just show the block list
148 $fields = [
149 'Target' => [
150 'type' => 'user',
151 'label-message' => 'ipaddressorusername',
152 'tabindex' => '1',
153 'size' => '45',
154 'default' => $this->target,
155 ],
156 'Options' => [
157 'type' => 'multiselect',
158 'options-messages' => $blockFilterOptions,
159 'flatlist' => true,
160 ],
161 ];
162
163 $fields['BlockType'] = [
164 'type' => 'select',
165 'label-message' => 'blocklist-type',
166 'options' => [
167 $this->msg( 'blocklist-type-opt-all' )->escaped() => '',
168 $this->msg( 'blocklist-type-opt-sitewide' )->escaped() => 'sitewide',
169 $this->msg( 'blocklist-type-opt-partial' )->escaped() => 'partial',
170 ],
171 'name' => 'blockType',
172 'cssclass' => 'mw-field-block-type',
173 ];
174
175 $fields['Limit'] = [
176 'type' => 'limitselect',
177 'label-message' => 'table_pager_limit_label',
178 'options' => $pager->getLimitSelectList(),
179 'name' => 'limit',
180 'default' => $pager->getLimit(),
181 'cssclass' => 'mw-field-limit mw-has-field-block-type',
182 ];
183
184 $form = HTMLForm::factory( 'ooui', $fields, $this->getContext() );
185 $form
186 ->setMethod( 'get' )
187 ->setTitle( $this->getPageTitle() ) // Remove subpage
188 ->setFormIdentifier( 'blocklist' )
189 ->setWrapperLegendMsg( 'ipblocklist-legend' )
190 ->setSubmitTextMsg( 'ipblocklist-submit' )
191 ->prepareForm()
192 ->displayForm( false );
193
194 $this->showList( $pager );
195 }
196
201 protected function getBlockListPager() {
202 $conds = [];
203 $db = $this->getDB();
204
205 // Add target conditions
206 if ( $this->target !== '' ) {
207 $target = $this->blockTargetFactory->newFromString( $this->target );
208 if ( $target ) {
209 $conds = $this->getTargetConds( $target );
210 }
211 }
212
213 // Apply filters
214 if ( in_array( 'userblocks', $this->options ) ) {
215 $namedUserConds = $db->expr( 'bt_user', '=', null );
216
217 // If temporary accounts are a known concept on this wiki,
218 // have the "Hide account blocks" filter exclude only named users (T380266).
219 if ( $this->tempUserConfig->isKnown() ) {
220 $namedUserConds = $namedUserConds->orExpr(
221 $this->tempUserConfig->getMatchCondition( $db, 'bt_user_text', IExpression::LIKE )
222 );
223 }
224
225 $conds[] = $namedUserConds;
226 }
227 if ( in_array( 'autoblocks', $this->options ) ) {
228 $conds['bl_parent_block_id'] = null;
229 }
230 if ( in_array( 'addressblocks', $this->options )
231 && in_array( 'rangeblocks', $this->options )
232 ) {
233 // Simpler conditions for only user blocks (T360864)
234 $conds[] = $db->expr( 'bt_user', '!=', null );
235 } elseif ( in_array( 'addressblocks', $this->options ) ) {
236 $conds[] = $db->expr( 'bt_user', '!=', null )->or( 'bt_range_start', '!=', null );
237 } elseif ( in_array( 'rangeblocks', $this->options ) ) {
238 $conds['bt_range_start'] = null;
239 }
240
241 if (
242 in_array( 'tempuserblocks', $this->options ) &&
243 $this->tempUserConfig->isKnown()
244 ) {
245 $conds[] = $db->expr( 'bt_user', '=', null )
246 ->orExpr(
247 $this->tempUserConfig->getMatchCondition( $db, 'bt_user_text', IExpression::NOT_LIKE )
248 );
249 }
250
251 $hideTemp = in_array( 'tempblocks', $this->options );
252 $hideIndef = in_array( 'indefblocks', $this->options );
253 if ( $hideTemp && $hideIndef ) {
254 // If both types are hidden, ensure query doesn't produce any results
255 $conds[] = '1=0';
256 } elseif ( $hideTemp ) {
257 $conds['bl_expiry'] = $db->getInfinity();
258 } elseif ( $hideIndef ) {
259 $conds[] = $db->expr( 'bl_expiry', '!=', $db->getInfinity() );
260 }
261
262 if ( $this->blockType === 'sitewide' ) {
263 $conds['bl_sitewide'] = 1;
264 } elseif ( $this->blockType === 'partial' ) {
265 $conds['bl_sitewide'] = 0;
266 }
267
268 return new BlockListPager(
269 $this->getContext(),
270 $this->blockActionInfo,
271 $this->blockRestrictionStore,
272 $this->blockTargetFactory,
273 $this->hideUserUtils,
274 $this->commentStore,
275 $this->linkBatchFactory,
276 $this->getLinkRenderer(),
277 $this->dbProvider,
278 $this->rowCommentFormatter,
279 $this->getSpecialPageFactory(),
280 $conds
281 );
282 }
283
297 private function getTargetConds( BlockTarget $target ) {
298 if ( $target instanceof AutoBlockTarget ) {
299 return [ 'bl_id' => $target->getId() ];
300 }
301 if ( $target instanceof BlockTargetWithIp ) {
302 $range = $target->toHexRange();
303 return [
304 $this->blockStore->getRangeCond( $range[0], $range[1] ),
305 'bt_auto' => 0
306 ];
307 }
308 if ( $target instanceof UserBlockTarget ) {
309 $user = $target->getUserIdentity();
310 if ( $user->getId() ) {
311 return [
312 'bt_user' => $user->getId(),
313 'bt_auto' => 0
314 ];
315 } else {
316 // No such user
317 return [ '1=0' ];
318 }
319 }
320 throw new InvalidArgumentException( 'Invalid block target type' );
321 }
322
327 protected function showList( BlockListPager $pager ) {
328 $out = $this->getOutput();
329
330 // Check for other blocks, i.e. global/tor blocks
331 $otherBlockLink = [];
332 $this->getHookRunner()->onOtherBlockLogLink( $otherBlockLink, $this->target );
333
334 // Show additional header for the local block only when other blocks exists.
335 // Not necessary in a standard installation without such extensions enabled
336 if ( count( $otherBlockLink ) ) {
337 $out->addHTML(
338 Html::element( 'h2', [], $this->msg( 'ipblocklist-localblock' )->text() ) . "\n"
339 );
340 }
341
342 if ( $pager->getNumRows() ) {
343 $out->addParserOutputContent(
344 $pager->getFullOutput(),
345 ParserOptions::newFromContext( $this->getContext() )
346 );
347 } elseif ( $this->target ) {
348 $out->addWikiMsg( 'ipblocklist-no-results' );
349 } else {
350 $out->addWikiMsg( 'ipblocklist-empty' );
351 }
352
353 if ( count( $otherBlockLink ) ) {
354 $out->addHTML(
355 Html::rawElement(
356 'h2',
357 [],
358 $this->msg( 'ipblocklist-otherblocks', count( $otherBlockLink ) )->parse()
359 ) . "\n"
360 );
361 $list = '';
362 foreach ( $otherBlockLink as $link ) {
363 $list .= Html::rawElement( 'li', [], $link ) . "\n";
364 }
365 $out->addHTML( Html::rawElement(
366 'ul',
367 [ 'class' => 'mw-ipblocklist-otherblocks' ],
368 $list
369 ) . "\n" );
370 }
371 }
372
373 protected function getGroupName() {
374 return 'users';
375 }
376
382 protected function getDB() {
383 return $this->dbProvider->getReplicaDatabase();
384 }
385}
386
388class_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:210
This class is a collection of static functions that serve two purposes:
Definition Html.php:57
getNumRows()
Get the number of rows in the result set.
getFullOutput()
Get the formatted result list, with navigation bars.
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.
getDB()
Return a IDatabase object for reading.
__construct(LinkBatchFactory $linkBatchFactory, DatabaseBlockStore $blockStore, BlockRestrictionStore $blockRestrictionStore, IConnectionProvider $dbProvider, CommentStore $commentStore, BlockTargetFactory $blockTargetFactory, HideUserUtils $hideUserUtils, BlockActionInfo $blockActionInfo, RowCommentFormatter $rowCommentFormatter, TempUserConfig $tempUserConfig)
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)