3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
21 namespace MediaWiki\Specials
;
23 use MediaWiki\Block\BlockActionInfo
;
24 use MediaWiki\Block\BlockRestrictionStore
;
25 use MediaWiki\Block\BlockUtils
;
26 use MediaWiki\Block\DatabaseBlock
;
27 use MediaWiki\Block\DatabaseBlockStore
;
28 use MediaWiki\Block\HideUserUtils
;
29 use MediaWiki\Cache\LinkBatchFactory
;
30 use MediaWiki\CommentFormatter\RowCommentFormatter
;
31 use MediaWiki\CommentStore\CommentStore
;
32 use MediaWiki\Html\Html
;
33 use MediaWiki\HTMLForm\HTMLForm
;
34 use MediaWiki\Pager\BlockListPager
;
35 use MediaWiki\SpecialPage\SpecialPage
;
36 use MediaWiki\User\TempUser\TempUserConfig
;
37 use Wikimedia\IPUtils
;
38 use Wikimedia\Rdbms\IConnectionProvider
;
39 use Wikimedia\Rdbms\IExpression
;
40 use Wikimedia\Rdbms\IReadableDatabase
;
43 * List of existing blocks
46 * @see SpecialAutoblockList
47 * @ingroup SpecialPage
49 class SpecialBlockList
extends SpecialPage
{
56 /** @var string|null */
59 private LinkBatchFactory
$linkBatchFactory;
60 private DatabaseBlockStore
$blockStore;
61 private BlockRestrictionStore
$blockRestrictionStore;
62 private IConnectionProvider
$dbProvider;
63 private CommentStore
$commentStore;
64 private BlockUtils
$blockUtils;
65 private HideUserUtils
$hideUserUtils;
66 private BlockActionInfo
$blockActionInfo;
67 private RowCommentFormatter
$rowCommentFormatter;
68 private TempUserConfig
$tempUserConfig;
70 public function __construct(
71 LinkBatchFactory
$linkBatchFactory,
72 DatabaseBlockStore
$blockStore,
73 BlockRestrictionStore
$blockRestrictionStore,
74 IConnectionProvider
$dbProvider,
75 CommentStore
$commentStore,
76 BlockUtils
$blockUtils,
77 HideUserUtils
$hideUserUtils,
78 BlockActionInfo
$blockActionInfo,
79 RowCommentFormatter
$rowCommentFormatter,
80 TempUserConfig
$tempUserConfig
82 parent
::__construct( 'BlockList' );
84 $this->linkBatchFactory
= $linkBatchFactory;
85 $this->blockStore
= $blockStore;
86 $this->blockRestrictionStore
= $blockRestrictionStore;
87 $this->dbProvider
= $dbProvider;
88 $this->commentStore
= $commentStore;
89 $this->blockUtils
= $blockUtils;
90 $this->hideUserUtils
= $hideUserUtils;
91 $this->blockActionInfo
= $blockActionInfo;
92 $this->rowCommentFormatter
= $rowCommentFormatter;
93 $this->tempUserConfig
= $tempUserConfig;
97 * @param string|null $par Title fragment
99 public function execute( $par ) {
101 $this->outputHeader();
102 $this->addHelpLink( 'Help:Blocking_users' );
103 $out = $this->getOutput();
104 $out->setPageTitleMsg( $this->msg( 'ipblocklist' ) );
105 $out->addModuleStyles( [ 'mediawiki.special' ] );
107 $request = $this->getRequest();
108 $par = $request->getVal( 'ip', $par ??
'' );
109 $this->target
= trim( $request->getVal( 'wpTarget', $par ) );
111 $this->options
= $request->getArray( 'wpOptions', [] );
112 $this->blockType
= $request->getVal( 'blockType' );
114 $action = $request->getText( 'action' );
116 if ( $action == 'unblock' ||
( $action == 'submit' && $request->wasPosted() ) ) {
117 // B/C @since 1.18: Unblock interface is now at Special:Unblock
118 $title = $this->getSpecialPageFactory()->getTitleForAlias( 'Unblock/' . $this->target
);
119 $out->redirect( $title->getFullURL() );
124 // Setup BlockListPager here to get the actual default Limit
125 $pager = $this->getBlockListPager();
127 $blockFilterOptions = [
128 'blocklist-tempblocks' => 'tempblocks',
129 'blocklist-indefblocks' => 'indefblocks',
130 'blocklist-autoblocks' => 'autoblocks',
131 'blocklist-addressblocks' => 'addressblocks',
132 'blocklist-rangeblocks' => 'rangeblocks',
135 if ( $this->tempUserConfig
->isKnown() ) {
136 // Clarify that "userblocks" excludes named users only if temporary accounts are known (T380266)
137 $blockFilterOptions['blocklist-nameduserblocks'] = 'userblocks';
138 $blockFilterOptions['blocklist-tempuserblocks'] = 'tempuserblocks';
140 $blockFilterOptions['blocklist-userblocks'] = 'userblocks';
143 // Just show the block list
147 'label-message' => 'ipaddressorusername',
150 'default' => $this->target
,
153 'type' => 'multiselect',
154 'options-messages' => $blockFilterOptions,
159 $fields['BlockType'] = [
161 'label-message' => 'blocklist-type',
163 $this->msg( 'blocklist-type-opt-all' )->escaped() => '',
164 $this->msg( 'blocklist-type-opt-sitewide' )->escaped() => 'sitewide',
165 $this->msg( 'blocklist-type-opt-partial' )->escaped() => 'partial',
167 'name' => 'blockType',
168 'cssclass' => 'mw-field-block-type',
172 'type' => 'limitselect',
173 'label-message' => 'table_pager_limit_label',
174 'options' => $pager->getLimitSelectList(),
176 'default' => $pager->getLimit(),
177 'cssclass' => 'mw-field-limit mw-has-field-block-type',
180 $form = HTMLForm
::factory( 'ooui', $fields, $this->getContext() );
183 ->setTitle( $this->getPageTitle() ) // Remove subpage
184 ->setFormIdentifier( 'blocklist' )
185 ->setWrapperLegendMsg( 'ipblocklist-legend' )
186 ->setSubmitTextMsg( 'ipblocklist-submit' )
188 ->displayForm( false );
190 $this->showList( $pager );
194 * Setup a new BlockListPager instance.
195 * @return BlockListPager
197 protected function getBlockListPager() {
199 $db = $this->getDB();
201 if ( $this->target
!== '' ) {
202 [ $target, $type ] = $this->blockUtils
->parseBlockTarget( $this->target
);
205 case DatabaseBlock
::TYPE_ID
:
206 case DatabaseBlock
::TYPE_AUTO
:
207 $conds['bl_id'] = $target;
210 case DatabaseBlock
::TYPE_IP
:
211 case DatabaseBlock
::TYPE_RANGE
:
212 [ $start, $end ] = IPUtils
::parseRange( $target );
213 $conds[] = $this->blockStore
->getRangeCond( $start, $end );
214 $conds['bt_auto'] = 0;
217 case DatabaseBlock
::TYPE_USER
:
218 if ( $target->getId() ) {
219 $conds['bt_user'] = $target->getId();
220 $conds['bt_auto'] = 0;
230 if ( in_array( 'userblocks', $this->options
) ) {
231 $namedUserConds = $db->expr( 'bt_user', '=', null );
233 // If temporary accounts are a known concept on this wiki,
234 // have the "Hide account blocks" filter exclude only named users (T380266).
235 if ( $this->tempUserConfig
->isKnown() ) {
236 $namedUserConds = $namedUserConds->orExpr(
237 $this->tempUserConfig
->getMatchCondition( $db, 'bt_user_text', IExpression
::LIKE
)
241 $conds[] = $namedUserConds;
243 if ( in_array( 'autoblocks', $this->options
) ) {
244 $conds['bl_parent_block_id'] = null;
246 if ( in_array( 'addressblocks', $this->options
)
247 && in_array( 'rangeblocks', $this->options
)
249 // Simpler conditions for only user blocks (T360864)
250 $conds[] = $db->expr( 'bt_user', '!=', null );
251 } elseif ( in_array( 'addressblocks', $this->options
) ) {
252 $conds[] = $db->expr( 'bt_user', '!=', null )->or( 'bt_range_start', '!=', null );
253 } elseif ( in_array( 'rangeblocks', $this->options
) ) {
254 $conds['bt_range_start'] = null;
258 in_array( 'tempuserblocks', $this->options
) &&
259 $this->tempUserConfig
->isKnown()
261 $conds[] = $db->expr( 'bt_user', '=', null )
263 $this->tempUserConfig
->getMatchCondition( $db, 'bt_user_text', IExpression
::NOT_LIKE
)
267 $hideTemp = in_array( 'tempblocks', $this->options
);
268 $hideIndef = in_array( 'indefblocks', $this->options
);
269 if ( $hideTemp && $hideIndef ) {
270 // If both types are hidden, ensure query doesn't produce any results
272 } elseif ( $hideTemp ) {
273 $conds['bl_expiry'] = $db->getInfinity();
274 } elseif ( $hideIndef ) {
275 $conds[] = $db->expr( 'bl_expiry', '!=', $db->getInfinity() );
278 if ( $this->blockType
=== 'sitewide' ) {
279 $conds['bl_sitewide'] = 1;
280 } elseif ( $this->blockType
=== 'partial' ) {
281 $conds['bl_sitewide'] = 0;
284 return new BlockListPager(
286 $this->blockActionInfo
,
287 $this->blockRestrictionStore
,
289 $this->hideUserUtils
,
291 $this->linkBatchFactory
,
292 $this->getLinkRenderer(),
294 $this->rowCommentFormatter
,
295 $this->getSpecialPageFactory(),
301 * Show the list of blocked accounts matching the actual filter.
302 * @param BlockListPager $pager The BlockListPager instance for this page
304 protected function showList( BlockListPager
$pager ) {
305 $out = $this->getOutput();
307 // Check for other blocks, i.e. global/tor blocks
308 $otherBlockLink = [];
309 $this->getHookRunner()->onOtherBlockLogLink( $otherBlockLink, $this->target
);
311 // Show additional header for the local block only when other blocks exists.
312 // Not necessary in a standard installation without such extensions enabled
313 if ( count( $otherBlockLink ) ) {
315 Html
::element( 'h2', [], $this->msg( 'ipblocklist-localblock' )->text() ) . "\n"
319 if ( $pager->getNumRows() ) {
320 $out->addParserOutputContent( $pager->getFullOutput() );
321 } elseif ( $this->target
) {
322 $out->addWikiMsg( 'ipblocklist-no-results' );
324 $out->addWikiMsg( 'ipblocklist-empty' );
327 if ( count( $otherBlockLink ) ) {
332 $this->msg( 'ipblocklist-otherblocks', count( $otherBlockLink ) )->parse()
336 foreach ( $otherBlockLink as $link ) {
337 $list .= Html
::rawElement( 'li', [], $link ) . "\n";
339 $out->addHTML( Html
::rawElement(
341 [ 'class' => 'mw-ipblocklist-otherblocks' ],
347 protected function getGroupName() {
352 * Return a IDatabase object for reading
354 * @return IReadableDatabase
356 protected function getDB() {
357 return $this->dbProvider
->getReplicaDatabase();
361 /** @deprecated class alias since 1.41 */
362 class_alias( SpecialBlockList
::class, 'SpecialBlockList' );