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