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