MediaWiki 1.41.2
SpecialBlock.php
Go to the documentation of this file.
1<?php
24namespace MediaWiki\Specials;
25
27use HtmlArmor;
28use HTMLForm;
30use Language;
59use Message;
60use OOUI\FieldLayout;
61use OOUI\HtmlSnippet;
62use OOUI\LabelWidget;
63use OOUI\Widget;
64use Wikimedia\IPUtils;
65use XmlSelect;
66
74
75 private BlockUtils $blockUtils;
76 private BlockPermissionCheckerFactory $blockPermissionCheckerFactory;
77 private BlockUserFactory $blockUserFactory;
78 private UserNameUtils $userNameUtils;
79 private UserNamePrefixSearch $userNamePrefixSearch;
80 private BlockActionInfo $blockActionInfo;
81 private TitleFormatter $titleFormatter;
82
86 protected $target;
87
89 protected $type;
90
92 protected $previousTarget;
93
96
98 protected $alreadyBlocked;
99
104 protected $preErrors = [];
105
106 private NamespaceInfo $namespaceInfo;
107
118 public function __construct(
119 BlockUtils $blockUtils,
120 BlockPermissionCheckerFactory $blockPermissionCheckerFactory,
121 BlockUserFactory $blockUserFactory,
122 UserNameUtils $userNameUtils,
123 UserNamePrefixSearch $userNamePrefixSearch,
124 BlockActionInfo $blockActionInfo,
125 TitleFormatter $titleFormatter,
126 NamespaceInfo $namespaceInfo
127 ) {
128 parent::__construct( 'Block', 'block' );
129
130 $this->blockUtils = $blockUtils;
131 $this->blockPermissionCheckerFactory = $blockPermissionCheckerFactory;
132 $this->blockUserFactory = $blockUserFactory;
133 $this->userNameUtils = $userNameUtils;
134 $this->userNamePrefixSearch = $userNamePrefixSearch;
135 $this->blockActionInfo = $blockActionInfo;
136 $this->titleFormatter = $titleFormatter;
137 $this->namespaceInfo = $namespaceInfo;
138 }
139
140 public function doesWrites() {
141 return true;
142 }
143
150 protected function checkExecutePermissions( User $user ) {
151 parent::checkExecutePermissions( $user );
152 // T17810: blocked admins should have limited access here
153 $status = $this->blockPermissionCheckerFactory
154 ->newBlockPermissionChecker( $this->target, $user )
155 ->checkBlockPermissions();
156 if ( $status !== true ) {
157 throw new ErrorPageError( 'badaccess', $status );
158 }
159 }
160
166 public function requiresUnblock() {
167 return false;
168 }
169
175 protected function setParameter( $par ) {
176 // Extract variables from the request. Try not to get into a situation where we
177 // need to extract *every* variable from the form just for processing here, but
178 // there are legitimate uses for some variables
179 $request = $this->getRequest();
180 [ $this->target, $this->type ] = self::getTargetAndTypeInternal( $par, $request );
181 if ( $this->target instanceof UserIdentity ) {
182 // Set the 'relevant user' in the skin, so it displays links like Contributions,
183 // User logs, UserRights, etc.
184 $this->getSkin()->setRelevantUser( $this->target );
185 }
186
187 [ $this->previousTarget, /*...*/ ] = $this->blockUtils
188 ->parseBlockTarget( $request->getVal( 'wpPreviousTarget' ) );
189 $this->requestedHideUser = $request->getBool( 'wpHideUser' );
190 }
191
197 protected function alterForm( HTMLForm $form ) {
198 $form->setHeaderHtml( '' );
199 $form->setSubmitDestructive();
200
201 $msg = $this->alreadyBlocked ? 'ipb-change-block' : 'ipbsubmit';
202 $form->setSubmitTextMsg( $msg );
203
204 $this->addHelpLink( 'Help:Blocking users' );
205
206 // Don't need to do anything if the form has been posted
207 if ( !$this->getRequest()->wasPosted() && $this->preErrors ) {
208 // Mimic error messages normally generated by the form
209 $form->addHeaderHtml( (string)new FieldLayout(
210 new Widget( [] ),
211 [
212 'align' => 'top',
213 'errors' => array_map( function ( $errMsg ) {
214 // @phan-suppress-next-line PhanParamTooFewUnpack Should infer non-emptiness
215 return new HtmlSnippet( $this->msg( ...$errMsg )->parse() );
216 }, $this->preErrors ),
217 ]
218 ) );
219 }
220 }
221
222 protected function getDisplayFormat() {
223 return 'ooui';
224 }
225
230 protected function getFormFields() {
231 $conf = $this->getConfig();
232 $blockAllowsUTEdit = $conf->get( MainConfigNames::BlockAllowsUTEdit );
233
234 $this->getOutput()->enableOOUI();
235
236 $user = $this->getUser();
237
238 $suggestedDurations = self::getSuggestedDurations();
239
240 $a = [];
241
242 $a['Target'] = [
243 'type' => 'user',
244 'ipallowed' => true,
245 'iprange' => true,
246 'id' => 'mw-bi-target',
247 'size' => '45',
248 'autofocus' => true,
249 'required' => true,
250 'placeholder' => $this->msg( 'block-target-placeholder' )->text(),
251 'validation-callback' => function ( $value, $alldata, $form ) {
252 $status = $this->blockUtils->validateTarget( $value );
253 if ( !$status->isOK() ) {
254 $errors = $status->getErrorsArray();
255
256 return $form->msg( ...$errors[0] );
257 }
258 return true;
259 },
260 'section' => 'target',
261 ];
262
263 $a['EditingRestriction'] = [
264 'type' => 'radio',
265 'cssclass' => 'mw-block-editing-restriction',
266 'default' => 'sitewide',
267 'options' => [
268 $this->msg( 'ipb-sitewide' )->escaped() .
269 new LabelWidget( [
270 'classes' => [ 'oo-ui-inline-help' ],
271 'label' => new HtmlSnippet( $this->msg( 'ipb-sitewide-help' )->parse() ),
272 ] ) => 'sitewide',
273 $this->msg( 'ipb-partial' )->escaped() .
274 new LabelWidget( [
275 'classes' => [ 'oo-ui-inline-help' ],
276 'label' => $this->msg( 'ipb-partial-help' )->text(),
277 ] ) => 'partial',
278 ],
279 'section' => 'actions',
280 ];
281
282 $a['PageRestrictions'] = [
283 'type' => 'titlesmultiselect',
284 'label' => $this->msg( 'ipb-pages-label' )->text(),
285 'exists' => true,
286 'max' => 10,
287 'cssclass' => 'mw-htmlform-checkradio-indent mw-block-partial-restriction',
288 'default' => '',
289 'showMissing' => false,
290 'excludeDynamicNamespaces' => true,
291 'input' => [
292 'autocomplete' => false
293 ],
294 'section' => 'actions',
295 ];
296
297 $a['NamespaceRestrictions'] = [
298 'type' => 'namespacesmultiselect',
299 'label' => $this->msg( 'ipb-namespaces-label' )->text(),
300 'exists' => true,
301 'cssclass' => 'mw-htmlform-checkradio-indent mw-block-partial-restriction',
302 'default' => '',
303 'input' => [
304 'autocomplete' => false
305 ],
306 'section' => 'actions',
307 ];
308
309 if ( $conf->get( MainConfigNames::EnablePartialActionBlocks ) ) {
310 $blockActions = $this->blockActionInfo->getAllBlockActions();
311 $a['ActionRestrictions'] = [
312 'type' => 'multiselect',
313 'cssclass' => 'mw-htmlform-checkradio-indent mw-block-partial-restriction mw-block-action-restriction',
314 'options-messages' => array_combine(
315 array_map( static function ( $action ) {
316 return "ipb-action-$action";
317 }, array_keys( $blockActions ) ),
318 $blockActions
319 ),
320 'section' => 'actions',
321 ];
322 }
323
324 $a['CreateAccount'] = [
325 'type' => 'check',
326 'cssclass' => 'mw-block-restriction',
327 'label-message' => 'ipbcreateaccount',
328 'default' => true,
329 'section' => 'details',
330 ];
331
332 if ( $this->blockPermissionCheckerFactory
333 ->newBlockPermissionChecker( null, $user )
334 ->checkEmailPermissions()
335 ) {
336 $a['DisableEmail'] = [
337 'type' => 'check',
338 'cssclass' => 'mw-block-restriction',
339 'label-message' => 'ipbemailban',
340 'section' => 'details',
341 ];
342 }
343
344 if ( $blockAllowsUTEdit ) {
345 $a['DisableUTEdit'] = [
346 'type' => 'check',
347 'cssclass' => 'mw-block-restriction',
348 'label-message' => 'ipb-disableusertalk',
349 'default' => false,
350 'section' => 'details',
351 ];
352 }
353
354 $defaultExpiry = $this->msg( 'ipb-default-expiry' )->inContentLanguage();
355 if ( $this->type === DatabaseBlock::TYPE_RANGE || $this->type === DatabaseBlock::TYPE_IP ) {
356 $defaultExpiryIP = $this->msg( 'ipb-default-expiry-ip' )->inContentLanguage();
357 if ( !$defaultExpiryIP->isDisabled() ) {
358 $defaultExpiry = $defaultExpiryIP;
359 }
360 }
361
362 $a['Expiry'] = [
363 'type' => 'expiry',
364 'required' => true,
365 'options' => $suggestedDurations,
366 'default' => $defaultExpiry->text(),
367 'section' => 'expiry',
368 ];
369
370 $a['Reason'] = [
371 'type' => 'selectandother',
372 // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
373 // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
374 // Unicode codepoints.
375 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT,
376 'maxlength-unit' => 'codepoints',
377 'options-message' => 'ipbreason-dropdown',
378 'section' => 'reason',
379 ];
380
381 $a['AutoBlock'] = [
382 'type' => 'check',
383 'label-message' => [
384 'ipbenableautoblock',
386 ],
387 'default' => true,
388 'section' => 'options',
389 ];
390
391 // Allow some users to hide name from block log, blocklist and listusers
392 if ( $this->getAuthority()->isAllowed( 'hideuser' ) ) {
393 $a['HideUser'] = [
394 'type' => 'check',
395 'label-message' => 'ipbhidename',
396 'cssclass' => 'mw-block-hideuser',
397 'section' => 'options',
398 ];
399 }
400
401 // Watchlist their user page? (Only if user is logged in)
402 if ( $user->isRegistered() ) {
403 $a['Watch'] = [
404 'type' => 'check',
405 'label-message' => 'ipbwatchuser',
406 'section' => 'options',
407 ];
408 }
409
410 $a['HardBlock'] = [
411 'type' => 'check',
412 'label-message' => 'ipb-hardblock',
413 'default' => false,
414 'section' => 'options',
415 ];
416
417 // This is basically a copy of the Target field, but the user can't change it, so we
418 // can see if the warnings we maybe showed to the user before still apply
419 $a['PreviousTarget'] = [
420 'type' => 'hidden',
421 'default' => false,
422 ];
423
424 // We'll turn this into a checkbox if we need to
425 $a['Confirm'] = [
426 'type' => 'hidden',
427 'default' => '',
428 'label-message' => 'ipb-confirm',
429 'cssclass' => 'mw-block-confirm',
430 ];
431
432 $this->maybeAlterFormDefaults( $a );
433
434 // Allow extensions to add more fields
435 $this->getHookRunner()->onSpecialBlockModifyFormFields( $this, $a );
436
437 return $a;
438 }
439
445 protected function maybeAlterFormDefaults( &$fields ) {
446 // This will be overwritten by request data
447 $fields['Target']['default'] = (string)$this->target;
448
449 if ( $this->target ) {
450 $status = $this->blockUtils->validateTarget( $this->target );
451 if ( !$status->isOK() ) {
452 $errors = $status->getErrorsArray();
453 $this->preErrors = array_merge( $this->preErrors, $errors );
454 }
455 }
456
457 // This won't be
458 $fields['PreviousTarget']['default'] = (string)$this->target;
459
460 $block = DatabaseBlock::newFromTarget( $this->target );
461
462 // Populate fields if there is a block that is not an autoblock; if it is a range
463 // block, only populate the fields if the range is the same as $this->target
464 if ( $block instanceof DatabaseBlock && $block->getType() !== DatabaseBlock::TYPE_AUTO
465 && ( $this->type != DatabaseBlock::TYPE_RANGE
466 || ( $this->target && $block->isBlocking( $this->target ) ) )
467 ) {
468 $fields['HardBlock']['default'] = $block->isHardblock();
469 $fields['CreateAccount']['default'] = $block->isCreateAccountBlocked();
470 $fields['AutoBlock']['default'] = $block->isAutoblocking();
471
472 if ( isset( $fields['DisableEmail'] ) ) {
473 $fields['DisableEmail']['default'] = $block->isEmailBlocked();
474 }
475
476 if ( isset( $fields['HideUser'] ) ) {
477 $fields['HideUser']['default'] = $block->getHideName();
478 }
479
480 if ( isset( $fields['DisableUTEdit'] ) ) {
481 $fields['DisableUTEdit']['default'] = !$block->isUsertalkEditAllowed();
482 }
483
484 // If the username was hidden (ipb_deleted == 1), don't show the reason
485 // unless this user also has rights to hideuser: T37839
486 if ( !$block->getHideName() || $this->getAuthority()->isAllowed( 'hideuser' ) ) {
487 $fields['Reason']['default'] = $block->getReasonComment()->text;
488 } else {
489 $fields['Reason']['default'] = '';
490 }
491
492 if ( $this->getRequest()->wasPosted() ) {
493 // Ok, so we got a POST submission asking us to reblock a user. So show the
494 // confirm checkbox; the user will only see it if they haven't previously
495 $fields['Confirm']['type'] = 'check';
496 } else {
497 // We got a target, but it wasn't a POST request, so the user must have gone
498 // to a link like [[Special:Block/User]]. We don't need to show the checkbox
499 // as long as they go ahead and block *that* user
500 $fields['Confirm']['default'] = 1;
501 }
502
503 if ( $block->getExpiry() == 'infinity' ) {
504 $fields['Expiry']['default'] = 'infinite';
505 } else {
506 $fields['Expiry']['default'] = wfTimestamp( TS_RFC2822, $block->getExpiry() );
507 }
508
509 if ( !$block->isSitewide() ) {
510 $fields['EditingRestriction']['default'] = 'partial';
511
512 $pageRestrictions = [];
513 $namespaceRestrictions = [];
514 foreach ( $block->getRestrictions() as $restriction ) {
515 if ( $restriction instanceof PageRestriction && $restriction->getTitle() ) {
516 $pageRestrictions[] = $restriction->getTitle()->getPrefixedText();
517 } elseif ( $restriction instanceof NamespaceRestriction &&
518 $this->namespaceInfo->exists( $restriction->getValue() )
519 ) {
520 $namespaceRestrictions[] = $restriction->getValue();
521 }
522 }
523
524 // Sort the restrictions so they are in alphabetical order.
525 sort( $pageRestrictions );
526 $fields['PageRestrictions']['default'] = implode( "\n", $pageRestrictions );
527 sort( $namespaceRestrictions );
528 $fields['NamespaceRestrictions']['default'] = implode( "\n", $namespaceRestrictions );
529
531 $actionRestrictions = [];
532 foreach ( $block->getRestrictions() as $restriction ) {
533 if ( $restriction instanceof ActionRestriction ) {
534 $actionRestrictions[] = $restriction->getValue();
535 }
536 }
537 $fields['ActionRestrictions']['default'] = $actionRestrictions;
538 }
539 }
540
541 $this->alreadyBlocked = true;
542 $this->preErrors[] = [ 'ipb-needreblock', wfEscapeWikiText( $block->getTargetName() ) ];
543 }
544
545 if ( $this->alreadyBlocked || $this->getRequest()->wasPosted()
546 || $this->getRequest()->getCheck( 'wpCreateAccount' )
547 ) {
548 $this->getOutput()->addJsConfigVars( 'wgCreateAccountDirty', true );
549 }
550
551 // We always need confirmation to do HideUser
552 if ( $this->requestedHideUser ) {
553 $fields['Confirm']['type'] = 'check';
554 unset( $fields['Confirm']['default'] );
555 $this->preErrors[] = [ 'ipb-confirmhideuser', 'ipb-confirmaction' ];
556 }
557
558 // Or if the user is trying to block themselves
559 if ( (string)$this->target === $this->getUser()->getName() ) {
560 $fields['Confirm']['type'] = 'check';
561 unset( $fields['Confirm']['default'] );
562 $this->preErrors[] = [ 'ipb-blockingself', 'ipb-confirmaction' ];
563 }
564 }
565
570 protected function preHtml() {
571 $this->getOutput()->addModuleStyles( [ 'mediawiki.special' ] );
572 $this->getOutput()->addModules( [ 'mediawiki.special.block' ] );
573
574 $blockCIDRLimit = $this->getConfig()->get( MainConfigNames::BlockCIDRLimit );
575 $text = $this->msg( 'blockiptext', $blockCIDRLimit['IPv4'], $blockCIDRLimit['IPv6'] )->parse();
576
577 $otherBlockMessages = [];
578 if ( $this->target !== null ) {
579 $targetName = $this->target;
580 if ( $this->target instanceof UserIdentity ) {
581 $targetName = $this->target->getName();
582 }
583 // Get other blocks, i.e. from GlobalBlocking or TorBlock extension
584 $this->getHookRunner()->onOtherBlockLogLink(
585 $otherBlockMessages, $targetName );
586
587 if ( count( $otherBlockMessages ) ) {
588 $s = Html::rawElement(
589 'h2',
590 [],
591 $this->msg( 'ipb-otherblocks-header', count( $otherBlockMessages ) )->parse()
592 ) . "\n";
593
594 $list = '';
595
596 foreach ( $otherBlockMessages as $link ) {
597 $list .= Html::rawElement( 'li', [], $link ) . "\n";
598 }
599
600 $s .= Html::rawElement(
601 'ul',
602 [ 'class' => 'mw-blockip-alreadyblocked' ],
603 $list
604 ) . "\n";
605
606 $text .= $s;
607 }
608 }
609
610 return $text;
611 }
612
617 protected function postHtml() {
618 $links = [];
619
620 $this->getOutput()->addModuleStyles( 'mediawiki.special' );
621
622 $linkRenderer = $this->getLinkRenderer();
623 // Link to the user's contributions, if applicable
624 if ( $this->target instanceof UserIdentity ) {
625 $contribsPage = SpecialPage::getTitleFor( 'Contributions', $this->target->getName() );
626 $links[] = $linkRenderer->makeLink(
627 $contribsPage,
628 $this->msg( 'ipb-blocklist-contribs', $this->target->getName() )->text()
629 );
630 }
631
632 // Link to unblock the specified user, or to a blank unblock form
633 if ( $this->target instanceof UserIdentity ) {
634 $message = $this->msg(
635 'ipb-unblock-addr',
636 wfEscapeWikiText( $this->target->getName() )
637 )->parse();
638 $list = SpecialPage::getTitleFor( 'Unblock', $this->target->getName() );
639 } else {
640 $message = $this->msg( 'ipb-unblock' )->parse();
641 $list = SpecialPage::getTitleFor( 'Unblock' );
642 }
643 $links[] = $linkRenderer->makeKnownLink(
644 $list,
645 new HtmlArmor( $message )
646 );
647
648 // Link to the block list
649 $links[] = $linkRenderer->makeKnownLink(
650 SpecialPage::getTitleFor( 'BlockList' ),
651 $this->msg( 'ipb-blocklist' )->text()
652 );
653
654 // Link to edit the block dropdown reasons, if applicable
655 if ( $this->getAuthority()->isAllowed( 'editinterface' ) ) {
656 $links[] = $linkRenderer->makeKnownLink(
657 $this->msg( 'ipbreason-dropdown' )->inContentLanguage()->getTitle(),
658 $this->msg( 'ipb-edit-dropdown' )->text(),
659 [],
660 [ 'action' => 'edit' ]
661 );
662 }
663
664 $text = Html::rawElement(
665 'p',
666 [ 'class' => 'mw-ipb-conveniencelinks' ],
667 $this->getLanguage()->pipeList( $links )
668 );
669
670 $userPage = self::getTargetUserTitle( $this->target );
671 if ( $userPage ) {
672 // Get relevant extracts from the block and suppression logs, if possible
673 $out = '';
674
675 LogEventsList::showLogExtract(
676 $out,
677 'block',
678 $userPage,
679 '',
680 [
681 'lim' => 10,
682 'msgKey' => [
683 'blocklog-showlog',
684 $this->titleFormatter->getText( $userPage ),
685 ],
686 'showIfEmpty' => false
687 ]
688 );
689 $text .= $out;
690
691 // Add suppression block entries if allowed
692 if ( $this->getAuthority()->isAllowed( 'suppressionlog' ) ) {
693 LogEventsList::showLogExtract(
694 $out,
695 'suppress',
696 $userPage,
697 '',
698 [
699 'lim' => 10,
700 'conds' => [ 'log_action' => [ 'block', 'reblock', 'unblock' ] ],
701 'msgKey' => [
702 'blocklog-showsuppresslog',
703 $this->titleFormatter->getText( $userPage ),
704 ],
705 'showIfEmpty' => false
706 ]
707 );
708
709 $text .= $out;
710 }
711 }
712
713 return $text;
714 }
715
722 protected static function getTargetUserTitle( $target ): ?PageReference {
723 if ( $target instanceof UserIdentity ) {
724 return PageReferenceValue::localReference( NS_USER, $target->getName() );
725 }
726
727 if ( is_string( $target ) && IPUtils::isIPAddress( $target ) ) {
728 return PageReferenceValue::localReference( NS_USER, $target );
729 }
730
731 return null;
732 }
733
748 public static function getTargetAndType( ?string $par, WebRequest $request = null ) {
749 wfDeprecated( __METHOD__, '1.36' );
750 return self::getTargetAndTypeInternal( $par, $request );
751 }
752
764 private static function getTargetAndTypeInternal( ?string $par, WebRequest $request = null ) {
765 if ( !$request instanceof WebRequest ) {
766 return MediaWikiServices::getInstance()->getBlockUtils()->parseBlockTarget( $par );
767 }
768
769 $possibleTargets = [
770 $request->getVal( 'wpTarget', null ),
771 $par,
772 $request->getVal( 'ip', null ),
773 // B/C @since 1.18
774 $request->getVal( 'wpBlockAddress', null ),
775 ];
776 foreach ( $possibleTargets as $possibleTarget ) {
777 $targetAndType = MediaWikiServices::getInstance()
778 ->getBlockUtils()
779 ->parseBlockTarget( $possibleTarget );
780 // If type is not null then target is valid
781 if ( $targetAndType[ 1 ] !== null ) {
782 break;
783 }
784 }
785 return $targetAndType;
786 }
787
796 public static function processForm( array $data, IContextSource $context ) {
797 $services = MediaWikiServices::getInstance();
798 return self::processFormInternal(
799 $data,
800 $context->getAuthority(),
801 $services->getBlockUserFactory(),
802 $services->getBlockUtils()
803 );
804 }
805
816 private static function processFormInternal(
817 array $data,
818 Authority $performer,
819 BlockUserFactory $blockUserFactory,
820 BlockUtils $blockUtils
821 ) {
822 // Temporarily access service container until the feature flag is removed: T280532
823 $enablePartialActionBlocks = MediaWikiServices::getInstance()
824 ->getMainConfig()->get( MainConfigNames::EnablePartialActionBlocks );
825
826 $isPartialBlock = isset( $data['EditingRestriction'] ) &&
827 $data['EditingRestriction'] === 'partial';
828
829 // This might have been a hidden field or a checkbox, so interesting data
830 // can come from it
831 $data['Confirm'] = !in_array( $data['Confirm'], [ '', '0', null, false ], true );
832
833 // If the user has done the form 'properly', they won't even have been given the
834 // option to suppress-block unless they have the 'hideuser' permission
835 if ( !isset( $data['HideUser'] ) ) {
836 $data['HideUser'] = false;
837 }
838
840 [ $target, $type ] = $blockUtils->parseBlockTarget( $data['Target'] );
841 if ( $type == DatabaseBlock::TYPE_USER ) {
842 $target = $target->getName();
843
844 // Give admins a heads-up before they go and block themselves. Much messier
845 // to do this for IPs, but it's pretty unlikely they'd ever get the 'block'
846 // permission anyway, although the code does allow for it.
847 // Note: Important to use $target instead of $data['Target']
848 // since both $data['PreviousTarget'] and $target are normalized
849 // but $data['target'] gets overridden by (non-normalized) request variable
850 // from previous request.
851 if ( $target === $performer->getUser()->getName() &&
852 ( $data['PreviousTarget'] !== $target || !$data['Confirm'] )
853 ) {
854 return [ 'ipb-blockingself', 'ipb-confirmaction' ];
855 }
856
857 if ( $data['HideUser'] && !$data['Confirm'] ) {
858 return [ 'ipb-confirmhideuser', 'ipb-confirmaction' ];
859 }
860 } elseif ( $type == DatabaseBlock::TYPE_IP ) {
861 $target = $target->getName();
862 } elseif ( $type != DatabaseBlock::TYPE_RANGE ) {
863 // This should have been caught in the form field validation
864 return [ 'badipaddress' ];
865 }
866
867 // Reason, to be passed to the block object. For default values of reason, see
868 // HTMLSelectAndOtherField::getDefault
869 $blockReason = $data['Reason'][0] ?? '';
870
871 $pageRestrictions = [];
872 $namespaceRestrictions = [];
873 $actionRestrictions = [];
874 if ( $isPartialBlock ) {
875 if ( isset( $data['PageRestrictions'] ) && $data['PageRestrictions'] !== '' ) {
876 $titles = explode( "\n", $data['PageRestrictions'] );
877 foreach ( $titles as $title ) {
878 $pageRestrictions[] = PageRestriction::newFromTitle( $title );
879 }
880 }
881 if ( isset( $data['NamespaceRestrictions'] ) && $data['NamespaceRestrictions'] !== '' ) {
882 $namespaceRestrictions = array_map( static function ( $id ) {
883 return new NamespaceRestriction( 0, (int)$id );
884 }, explode( "\n", $data['NamespaceRestrictions'] ) );
885 }
886 if (
887 $enablePartialActionBlocks &&
888 isset( $data['ActionRestrictions'] ) &&
889 $data['ActionRestrictions'] !== ''
890 ) {
891 $actionRestrictions = array_map( static function ( $id ) {
892 return new ActionRestriction( 0, $id );
893 }, $data['ActionRestrictions'] );
894 }
895 }
896 $restrictions = array_merge( $pageRestrictions, $namespaceRestrictions, $actionRestrictions );
897
898 if ( !isset( $data['Tags'] ) ) {
899 $data['Tags'] = [];
900 }
901
902 $blockOptions = [
903 'isCreateAccountBlocked' => $data['CreateAccount'],
904 'isHardBlock' => $data['HardBlock'],
905 'isAutoblocking' => $data['AutoBlock'],
906 'isHideUser' => $data['HideUser'],
907 'isPartial' => $isPartialBlock,
908 ];
909
910 if ( isset( $data['DisableUTEdit'] ) ) {
911 $blockOptions['isUserTalkEditBlocked'] = $data['DisableUTEdit'];
912 }
913 if ( isset( $data['DisableEmail'] ) ) {
914 $blockOptions['isEmailBlocked'] = $data['DisableEmail'];
915 }
916
917 $blockUser = $blockUserFactory->newBlockUser(
918 $target,
919 $performer,
920 $data['Expiry'],
921 $blockReason,
922 $blockOptions,
923 $restrictions,
924 $data['Tags']
925 );
926
927 // Indicates whether the user is confirming the block and is aware of
928 // the conflict (did not change the block target in the meantime)
929 $blockNotConfirmed = !$data['Confirm'] || ( array_key_exists( 'PreviousTarget', $data )
930 && $data['PreviousTarget'] !== $target );
931
932 // Special case for API - T34434
933 $reblockNotAllowed = ( array_key_exists( 'Reblock', $data ) && !$data['Reblock'] );
934
935 $doReblock = !$blockNotConfirmed && !$reblockNotAllowed;
936
937 $status = $blockUser->placeBlock( $doReblock );
938 if ( !$status->isOK() ) {
939 return $status;
940 }
941
942 if (
943 // Can't watch a range block
944 $type != DatabaseBlock::TYPE_RANGE
945
946 // Technically a wiki can be configured to allow anonymous users to place blocks,
947 // in which case the 'Watch' field isn't included in the form shown, and we should
948 // not try to access it.
949 && array_key_exists( 'Watch', $data )
950 && $data['Watch']
951 ) {
952 MediaWikiServices::getInstance()->getWatchlistManager()->addWatchIgnoringRights(
953 $performer->getUser(),
954 Title::makeTitle( NS_USER, $target )
955 );
956 }
957
958 return true;
959 }
960
971 public static function getSuggestedDurations( Language $lang = null, $includeOther = true ) {
972 $msg = $lang === null
973 ? wfMessage( 'ipboptions' )->inContentLanguage()->text()
974 : wfMessage( 'ipboptions' )->inLanguage( $lang )->text();
975
976 if ( $msg == '-' ) {
977 return [];
978 }
979
980 $a = XmlSelect::parseOptionsMessage( $msg );
981
982 if ( $a && $includeOther ) {
983 // If options exist, add other to the end instead of the beginning (which
984 // is what happens by default).
985 $a[ wfMessage( 'ipbother' )->text() ] = 'other';
986 }
987
988 return $a;
989 }
990
1000 public static function parseExpiryInput( $expiry ) {
1001 return BlockUser::parseExpiryInput( $expiry );
1002 }
1003
1011 public static function canBlockEmail( UserIdentity $user ) {
1013 ->getBlockPermissionCheckerFactory()
1014 ->newBlockPermissionChecker( null, User::newFromIdentity( $user ) )
1015 ->checkEmailPermissions();
1016 }
1017
1024 public function onSubmit( array $data, HTMLForm $form = null ) {
1025 return self::processFormInternal(
1026 $data,
1027 $this->getAuthority(),
1028 $this->blockUserFactory,
1029 $this->blockUtils
1030 );
1031 }
1032
1037 public function onSuccess() {
1038 $out = $this->getOutput();
1039 $out->setPageTitleMsg( $this->msg( 'blockipsuccesssub' ) );
1040 $out->addWikiMsg( 'blockipsuccesstext', wfEscapeWikiText( $this->target ) );
1041 }
1042
1051 public function prefixSearchSubpages( $search, $limit, $offset ) {
1052 $search = $this->userNameUtils->getCanonical( $search );
1053 if ( !$search ) {
1054 // No prefix suggestion for invalid user
1055 return [];
1056 }
1057 // Autocomplete subpage as user list - public to allow caching
1058 return $this->userNamePrefixSearch
1059 ->search( UserNamePrefixSearch::AUDIENCE_PUBLIC, $search, $limit, $offset );
1060 }
1061
1062 protected function getGroupName() {
1063 return 'users';
1064 }
1065}
1066
1070class_alias( SpecialBlock::class, 'SpecialBlock' );
getAuthority()
const NS_USER
Definition Defines.php:66
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:88
An error page which can definitely be safely rendered using the OutputPage.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:158
addHeaderHtml( $html, $section=null)
Add HTML to the header, inside the form.
Definition HTMLForm.php:902
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
setSubmitDestructive()
Identify that the submit button in the form has a destructive action.
setHeaderHtml( $html, $section=null)
Set header HTML, inside the form.
Definition HTMLForm.php:922
Marks HTML that shouldn't be escaped.
Definition HtmlArmor.php:30
Base class for language-specific code.
Definition Language.php:63
Defines the actions that can be blocked by a partial block.
Handles the backend logic of blocking users.
Definition BlockUser.php:53
static parseExpiryInput(string $expiry)
Convert a submitted expiry time, which may be relative ("2 weeks", etc) or absolute ("24 May 2034",...
Backend class for blocking utils.
parseBlockTarget( $target)
From an existing block, get the target and the type of target.
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
getType()
Get the type of target for this particular block.int|null AbstractBlock::TYPE_ constant,...
Restriction for partial blocks of actions.
Handle database storage of comments such as edit summaries and log reasons.
This class is a collection of static functions that serve two purposes:
Definition Html.php:57
A class containing constants representing the names of configuration variables.
const AutoblockExpiry
Name constant for the AutoblockExpiry setting, for use with Config::get()
const BlockAllowsUTEdit
Name constant for the BlockAllowsUTEdit setting, for use with Config::get()
const BlockCIDRLimit
Name constant for the BlockCIDRLimit setting, for use with Config::get()
const EnablePartialActionBlocks
Name constant for the EnablePartialActionBlocks setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
Immutable value object representing a page reference.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Special page which uses an HTMLForm to handle processing.
string null $par
The sub-page of the special page.
Parent class for all special pages.
getSkin()
Shortcut to get the skin being used for this instance.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
getUser()
Shortcut to get the User executing this instance.
getConfig()
Shortcut to get main config object.
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.
getAuthority()
Shortcut to get the Authority executing this instance.
getLanguage()
Shortcut to get user's language.
getName()
Get the name of this Special Page.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
A special page that allows users with 'block' right to block users from editing pages and other actio...
__construct(BlockUtils $blockUtils, BlockPermissionCheckerFactory $blockPermissionCheckerFactory, BlockUserFactory $blockUserFactory, UserNameUtils $userNameUtils, UserNamePrefixSearch $userNamePrefixSearch, BlockActionInfo $blockActionInfo, TitleFormatter $titleFormatter, NamespaceInfo $namespaceInfo)
static parseExpiryInput( $expiry)
Convert a submitted expiry time, which may be relative ("2 weeks", etc) or absolute ("24 May 2034",...
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
onSuccess()
Do something exciting on successful processing of the form, most likely to show a confirmation messag...
onSubmit(array $data, HTMLForm $form=null)
Process the form on POST submission.
preHtml()
Add header elements like block log entries, etc.
static canBlockEmail(UserIdentity $user)
Can we do an email block?
int $type
DatabaseBlock::TYPE_ constant.
static getSuggestedDurations(Language $lang=null, $includeOther=true)
Get an array of suggested block durations from MediaWiki:Ipboptions.
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
User string $previousTarget
The previous block target.
alterForm(HTMLForm $form)
Customizes the HTMLForm a bit.
postHtml()
Add footer elements to the form.
requiresUnblock()
We allow certain special cases where user is blocked.
UserIdentity string null $target
User to be blocked, as passed either by parameter (url?wpTarget=Foo) or as subpage (Special:Block/Foo...
getDisplayFormat()
Get display format for the form.
setParameter( $par)
Handle some magic here.
maybeAlterFormDefaults(&$fields)
If the user has already been blocked with similar settings, load that block and change the defaults f...
checkExecutePermissions(User $user)
Check that the user can unblock themselves if they are trying to do so.
doesWrites()
Indicates whether this special page may perform database writes.
static getTargetUserTitle( $target)
Get a user page target for things like logs.
static getTargetAndType(?string $par, WebRequest $request=null)
Get the target and type, given the request and the subpage parameter.
bool $requestedHideUser
Whether the previous submission of the form asked for HideUser.
static processForm(array $data, IContextSource $context)
Given the form data, actually implement a block.
getFormFields()
Get the HTMLForm descriptor array for the block form.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:58
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Represents a title within MediaWiki.
Definition Title.php:76
Handles searching prefixes of user names.
UserNameUtils service.
internal since 1.36
Definition User.php:98
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:144
static durationParam( $duration)
Definition Message.php:1165
Class for generating HTML <select> or <datalist> elements.
Definition XmlSelect.php:28
Interface for objects which can provide a MediaWiki context on request.
newBlockUser( $target, Authority $performer, string $expiry, string $reason='', array $blockOptions=[], array $blockRestrictions=[], $tags=[])
Create BlockUser.
Interface for objects (potentially) representing a page that can be viewable and linked to on a wiki.
This interface represents the authority associated the current execution context, such as a web reque...
Definition Authority.php:37
getUser()
Returns the performer of the actions associated with this authority.
A title formatter service for MediaWiki.
Interface for objects representing user identity.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Ge...