MediaWiki master
SpecialBlockList.php
Go to the documentation of this file.
1<?php
24namespace MediaWiki\Specials;
25
41use Wikimedia\IPUtils;
44
51 protected $target;
52
53 protected $options;
54
55 protected $blockType;
56
57 private LinkBatchFactory $linkBatchFactory;
58 private DatabaseBlockStore $blockStore;
59 private BlockRestrictionStore $blockRestrictionStore;
60 private IConnectionProvider $dbProvider;
61 private CommentStore $commentStore;
62 private BlockUtils $blockUtils;
63 private HideUserUtils $hideUserUtils;
64 private BlockActionInfo $blockActionInfo;
65 private RowCommentFormatter $rowCommentFormatter;
66
67 public function __construct(
68 LinkBatchFactory $linkBatchFactory,
69 DatabaseBlockStore $blockStore,
70 BlockRestrictionStore $blockRestrictionStore,
71 IConnectionProvider $dbProvider,
72 CommentStore $commentStore,
73 BlockUtils $blockUtils,
74 HideUserUtils $hideUserUtils,
75 BlockActionInfo $blockActionInfo,
76 RowCommentFormatter $rowCommentFormatter
77 ) {
78 parent::__construct( 'BlockList' );
79
80 $this->linkBatchFactory = $linkBatchFactory;
81 $this->blockStore = $blockStore;
82 $this->blockRestrictionStore = $blockRestrictionStore;
83 $this->dbProvider = $dbProvider;
84 $this->commentStore = $commentStore;
85 $this->blockUtils = $blockUtils;
86 $this->hideUserUtils = $hideUserUtils;
87 $this->blockActionInfo = $blockActionInfo;
88 $this->rowCommentFormatter = $rowCommentFormatter;
89 }
90
94 public function execute( $par ) {
95 $this->setHeaders();
96 $this->outputHeader();
97 $this->addHelpLink( 'Help:Blocking_users' );
98 $out = $this->getOutput();
99 $out->setPageTitleMsg( $this->msg( 'ipblocklist' ) );
100 $out->addModuleStyles( [ 'mediawiki.special' ] );
101
102 $request = $this->getRequest();
103 $par = $request->getVal( 'ip', $par ?? '' );
104 $this->target = trim( $request->getVal( 'wpTarget', $par ) );
105
106 $this->options = $request->getArray( 'wpOptions', [] );
107 $this->blockType = $request->getVal( 'blockType' );
108
109 $action = $request->getText( 'action' );
110
111 if ( $action == 'unblock' || ( $action == 'submit' && $request->wasPosted() ) ) {
112 // B/C @since 1.18: Unblock interface is now at Special:Unblock
113 $title = $this->getSpecialPageFactory()->getTitleForAlias( 'Unblock/' . $this->target );
114 $out->redirect( $title->getFullURL() );
115
116 return;
117 }
118
119 // Setup BlockListPager here to get the actual default Limit
120 $pager = $this->getBlockListPager();
121
122 // Just show the block list
123 $fields = [
124 'Target' => [
125 'type' => 'user',
126 'label-message' => 'ipaddressorusername',
127 'tabindex' => '1',
128 'size' => '45',
129 'default' => $this->target,
130 ],
131 'Options' => [
132 'type' => 'multiselect',
133 'options-messages' => [
134 'blocklist-tempblocks' => 'tempblocks',
135 'blocklist-indefblocks' => 'indefblocks',
136 'blocklist-autoblocks' => 'autoblocks',
137 'blocklist-userblocks' => 'userblocks',
138 'blocklist-addressblocks' => 'addressblocks',
139 'blocklist-rangeblocks' => 'rangeblocks',
140 ],
141 'flatlist' => true,
142 ],
143 ];
144
145 $fields['BlockType'] = [
146 'type' => 'select',
147 'label-message' => 'blocklist-type',
148 'options' => [
149 $this->msg( 'blocklist-type-opt-all' )->escaped() => '',
150 $this->msg( 'blocklist-type-opt-sitewide' )->escaped() => 'sitewide',
151 $this->msg( 'blocklist-type-opt-partial' )->escaped() => 'partial',
152 ],
153 'name' => 'blockType',
154 'cssclass' => 'mw-field-block-type',
155 ];
156
157 $fields['Limit'] = [
158 'type' => 'limitselect',
159 'label-message' => 'table_pager_limit_label',
160 'options' => $pager->getLimitSelectList(),
161 'name' => 'limit',
162 'default' => $pager->getLimit(),
163 'cssclass' => 'mw-field-limit mw-has-field-block-type',
164 ];
165
166 $form = HTMLForm::factory( 'ooui', $fields, $this->getContext() );
167 $form
168 ->setMethod( 'get' )
169 ->setTitle( $this->getPageTitle() ) // Remove subpage
170 ->setFormIdentifier( 'blocklist' )
171 ->setWrapperLegendMsg( 'ipblocklist-legend' )
172 ->setSubmitTextMsg( 'ipblocklist-submit' )
173 ->prepareForm()
174 ->displayForm( false );
175
176 $this->showList( $pager );
177 }
178
183 protected function getBlockListPager() {
184 $readStage = $this->getConfig()
186 if ( $readStage === SCHEMA_COMPAT_READ_OLD ) {
187 $bl_deleted = 'ipb_deleted';
188 $bl_id = 'ipb_id';
189 $bt_auto = 'ipb_auto';
190 $bt_user = 'ipb_user';
191 $bl_expiry = 'ipb_expiry';
192 $bl_sitewide = 'ipb_sitewide';
193 } elseif ( $readStage === SCHEMA_COMPAT_READ_NEW ) {
194 $bl_deleted = 'bl_deleted';
195 $bl_id = 'bl_id';
196 $bt_auto = 'bt_auto';
197 $bt_user = 'bt_user';
198 $bl_expiry = 'bl_expiry';
199 $bl_sitewide = 'bl_sitewide';
200 } else {
201 throw new ConfigException(
202 '$wgBlockTargetMigrationStage has an invalid read stage' );
203 }
204
205 $conds = [];
206 $db = $this->getDB();
207
208 if ( $this->target !== '' ) {
209 [ $target, $type ] = $this->blockUtils->parseBlockTarget( $this->target );
210
211 switch ( $type ) {
212 case DatabaseBlock::TYPE_ID:
213 case DatabaseBlock::TYPE_AUTO:
214 $conds[$bl_id] = $target;
215 break;
216
217 case DatabaseBlock::TYPE_IP:
218 case DatabaseBlock::TYPE_RANGE:
219 [ $start, $end ] = IPUtils::parseRange( $target );
220 $conds[] = $this->blockStore->getRangeCond( $start, $end,
221 DatabaseBlockStore::SCHEMA_CURRENT );
222 $conds[$bt_auto] = 0;
223 break;
224
225 case DatabaseBlock::TYPE_USER:
226 if ( $target->getId() ) {
227 $conds[$bt_user] = $target->getId();
228 $conds[$bt_auto] = 0;
229 } else {
230 // No such user
231 $conds[] = '1=0';
232 }
233 break;
234 }
235 }
236
237 // Apply filters
238 if ( in_array( 'userblocks', $this->options ) ) {
239 if ( $readStage === SCHEMA_COMPAT_READ_OLD ) {
240 $conds['ipb_user'] = 0;
241 } else {
242 $conds['bt_user'] = null;
243 }
244 }
245 if ( in_array( 'autoblocks', $this->options ) ) {
246 if ( $readStage === SCHEMA_COMPAT_READ_OLD ) {
247 // ipb_parent_block_id = 0 because of T282890
248 $conds['ipb_parent_block_id'] = [ null, 0 ];
249 } else {
250 $conds['bl_parent_block_id'] = null;
251 }
252 }
253 if ( in_array( 'addressblocks', $this->options )
254 && in_array( 'rangeblocks', $this->options )
255 ) {
256 // Simpler conditions for only user blocks (T360864)
257 if ( $readStage === SCHEMA_COMPAT_READ_OLD ) {
258 $conds[] = "ipb_user != 0";
259 } else {
260 $conds[] = "bt_user IS NOT NULL";
261 }
262 } elseif ( in_array( 'addressblocks', $this->options ) ) {
263 if ( $readStage === SCHEMA_COMPAT_READ_OLD ) {
264 $conds[] = "ipb_user != 0 OR ipb_range_end > ipb_range_start";
265 } else {
266 $conds[] = "bt_user IS NOT NULL OR bt_range_start IS NOT NULL";
267 }
268 } elseif ( in_array( 'rangeblocks', $this->options ) ) {
269 if ( $readStage === SCHEMA_COMPAT_READ_OLD ) {
270 $conds[] = "ipb_range_end = ipb_range_start";
271 } else {
272 $conds['bt_range_start'] = null;
273 }
274 }
275
276 $hideTemp = in_array( 'tempblocks', $this->options );
277 $hideIndef = in_array( 'indefblocks', $this->options );
278 if ( $hideTemp && $hideIndef ) {
279 // If both types are hidden, ensure query doesn't produce any results
280 $conds[] = '1=0';
281 } elseif ( $hideTemp ) {
282 $conds[$bl_expiry] = $db->getInfinity();
283 } elseif ( $hideIndef ) {
284 $conds[] = $db->expr( $bl_expiry, '!=', $db->getInfinity() );
285 }
286
287 if ( $this->blockType === 'sitewide' ) {
288 $conds[$bl_sitewide] = 1;
289 } elseif ( $this->blockType === 'partial' ) {
290 $conds[$bl_sitewide] = 0;
291 }
292
293 return new BlockListPager(
294 $this->getContext(),
295 $this->blockActionInfo,
296 $this->blockRestrictionStore,
297 $this->blockUtils,
298 $this->hideUserUtils,
299 $this->commentStore,
300 $this->linkBatchFactory,
301 $this->getLinkRenderer(),
302 $this->dbProvider,
303 $this->rowCommentFormatter,
304 $this->getSpecialPageFactory(),
305 $conds
306 );
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( $pager->getFullOutput() );
330 } elseif ( $this->target ) {
331 $out->addWikiMsg( 'ipblocklist-no-results' );
332 } else {
333 $out->addWikiMsg( 'ipblocklist-empty' );
334 }
335
336 if ( count( $otherBlockLink ) ) {
337 $out->addHTML(
338 Html::rawElement(
339 'h2',
340 [],
341 $this->msg( 'ipblocklist-otherblocks', count( $otherBlockLink ) )->parse()
342 ) . "\n"
343 );
344 $list = '';
345 foreach ( $otherBlockLink as $link ) {
346 $list .= Html::rawElement( 'li', [], $link ) . "\n";
347 }
348 $out->addHTML( Html::rawElement(
349 'ul',
350 [ 'class' => 'mw-ipblocklist-otherblocks' ],
351 $list
352 ) . "\n" );
353 }
354 }
355
356 protected function getGroupName() {
357 return 'users';
358 }
359
365 protected function getDB() {
366 return $this->dbProvider->getReplicaDatabase();
367 }
368}
369
371class_alias( SpecialBlockList::class, 'SpecialBlockList' );
const SCHEMA_COMPAT_READ_NEW
Definition Defines.php:279
const SCHEMA_COMPAT_READ_OLD
Definition Defines.php:275
const SCHEMA_COMPAT_READ_MASK
Definition Defines.php:281
Defines the actions that can be blocked by a partial block.
Backend class for blocking utils.
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
Helpers for building queries that determine whether a user is hidden.
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.
Exceptions for config failures.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:206
This class is a collection of static functions that serve two purposes:
Definition Html.php:56
A class containing constants representing the names of configuration variables.
const BlockTargetMigrationStage
Name constant for the BlockTargetMigrationStage setting, for use with Config::get()
getNumRows()
Get the number of rows in the result set.
getFullOutput()
Get the formatted result list, with navigation bars.
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.
getConfig()
Shortcut to get main config 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 Per default the message key is the canonical name o...
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
A special page that lists existing blocks.
showList(BlockListPager $pager)
Show the list of blocked accounts matching the actual filter.
__construct(LinkBatchFactory $linkBatchFactory, DatabaseBlockStore $blockStore, BlockRestrictionStore $blockRestrictionStore, IConnectionProvider $dbProvider, CommentStore $commentStore, BlockUtils $blockUtils, HideUserUtils $hideUserUtils, BlockActionInfo $blockActionInfo, RowCommentFormatter $rowCommentFormatter)
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...
Provide primary and replica IDatabase connections.
A database connection without write operations.
element(SerializerNode $parent, SerializerNode $node, $contents)
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Ge...