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