MediaWiki  master
SpecialBlock.php
Go to the documentation of this file.
1 <?php
41 use 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 
84  protected $requestedHideUser;
85 
87  protected $alreadyBlocked;
88 
90  protected $preErrors = [];
91 
93  private $namespaceInfo;
94 
105  public function __construct(
106  BlockUtils $blockUtils,
107  BlockPermissionCheckerFactory $blockPermissionCheckerFactory,
108  BlockUserFactory $blockUserFactory,
109  UserNameUtils $userNameUtils,
110  UserNamePrefixSearch $userNamePrefixSearch,
111  BlockActionInfo $blockActionInfo,
112  TitleFormatter $titleFormatter,
113  NamespaceInfo $namespaceInfo
114  ) {
115  parent::__construct( 'Block', 'block' );
116 
117  $this->blockUtils = $blockUtils;
118  $this->blockPermissionCheckerFactory = $blockPermissionCheckerFactory;
119  $this->blockUserFactory = $blockUserFactory;
120  $this->userNameUtils = $userNameUtils;
121  $this->userNamePrefixSearch = $userNamePrefixSearch;
122  $this->blockActionInfo = $blockActionInfo;
123  $this->titleFormatter = $titleFormatter;
124  $this->namespaceInfo = $namespaceInfo;
125  }
126 
127  public function doesWrites() {
128  return true;
129  }
130 
137  protected function checkExecutePermissions( User $user ) {
138  parent::checkExecutePermissions( $user );
139  # T17810: blocked admins should have limited access here
140  $status = $this->blockPermissionCheckerFactory
141  ->newBlockPermissionChecker( $this->target, $user )
142  ->checkBlockPermissions();
143  if ( $status !== true ) {
144  throw new ErrorPageError( 'badaccess', $status );
145  }
146  }
147 
153  public function requiresUnblock() {
154  return false;
155  }
156 
162  protected function setParameter( $par ) {
163  # Extract variables from the request. Try not to get into a situation where we
164  # need to extract *every* variable from the form just for processing here, but
165  # there are legitimate uses for some variables
166  $request = $this->getRequest();
167  list( $this->target, $this->type ) = self::getTargetAndType( $par, $request );
168  if ( $this->target instanceof UserIdentity ) {
169  # Set the 'relevant user' in the skin, so it displays links like Contributions,
170  # User logs, UserRights, etc.
171  $this->getSkin()->setRelevantUser( $this->target );
172  }
173 
174  list( $this->previousTarget, /*...*/ ) = $this->blockUtils
175  ->parseBlockTarget( $request->getVal( 'wpPreviousTarget' ) );
176  $this->requestedHideUser = $request->getBool( 'wpHideUser' );
177  }
178 
184  protected function alterForm( HTMLForm $form ) {
185  $form->setHeaderText( '' );
186  $form->setSubmitDestructive();
187 
188  $msg = $this->alreadyBlocked ? 'ipb-change-block' : 'ipbsubmit';
189  $form->setSubmitTextMsg( $msg );
190 
191  $this->addHelpLink( 'Help:Blocking users' );
192 
193  # Don't need to do anything if the form has been posted
194  if ( !$this->getRequest()->wasPosted() && $this->preErrors ) {
195  if ( $this->preErrors ) {
196  # Mimic error messages normally generated by the form
197  $form->addHeaderText( (string)new OOUI\FieldLayout(
198  new OOUI\Widget( [] ),
199  [
200  'align' => 'top',
201  'errors' => array_map( function ( $errMsg ) {
202  return new OOUI\HtmlSnippet( $this->msg( ...$errMsg )->parse() );
203  }, $this->preErrors ),
204  ]
205  ) );
206  }
207  }
208  }
209 
210  protected function getDisplayFormat() {
211  return 'ooui';
212  }
213 
218  protected function getFormFields() {
219  $conf = $this->getConfig();
220  $blockAllowsUTEdit = $conf->get( MainConfigNames::BlockAllowsUTEdit );
221  $this->getOutput()->addJsConfigVars(
222  'wgEnablePartialActionBlocks',
223  $conf->get( MainConfigNames::EnablePartialActionBlocks )
224  );
225 
226  $this->getOutput()->enableOOUI();
227 
228  $user = $this->getUser();
229 
230  $suggestedDurations = self::getSuggestedDurations();
231 
232  $a = [];
233 
234  $a['Target'] = [
235  'type' => 'user',
236  'ipallowed' => true,
237  'iprange' => true,
238  'id' => 'mw-bi-target',
239  'size' => '45',
240  'autofocus' => true,
241  'required' => true,
242  'placeholder' => $this->msg( 'block-target-placeholder' )->text(),
243  'validation-callback' => function ( $value, $alldata, $form ) {
244  $status = $this->blockUtils->validateTarget( $value );
245  if ( !$status->isOK() ) {
246  $errors = $status->getErrorsArray();
247 
248  return $form->msg( ...$errors[0] );
249  }
250  return true;
251  },
252  'section' => 'target',
253  ];
254 
255  $a['EditingRestriction'] = [
256  'type' => 'radio',
257  'cssclass' => 'mw-block-editing-restriction',
258  'default' => 'sitewide',
259  'options' => [
260  $this->msg( 'ipb-sitewide' )->escaped() .
261  new \OOUI\LabelWidget( [
262  'classes' => [ 'oo-ui-inline-help' ],
263  'label' => new \OOUI\HtmlSnippet( $this->msg( 'ipb-sitewide-help' )->parse() ),
264  ] ) => 'sitewide',
265  $this->msg( 'ipb-partial' )->escaped() .
266  new \OOUI\LabelWidget( [
267  'classes' => [ 'oo-ui-inline-help' ],
268  'label' => $this->msg( 'ipb-partial-help' )->text(),
269  ] ) => 'partial',
270  ],
271  'section' => 'actions',
272  ];
273 
274  $a['PageRestrictions'] = [
275  'type' => 'titlesmultiselect',
276  'label' => $this->msg( 'ipb-pages-label' )->text(),
277  'exists' => true,
278  'max' => 10,
279  'cssclass' => 'mw-htmlform-checkradio-indent mw-block-partial-restriction',
280  'showMissing' => false,
281  'excludeDynamicNamespaces' => true,
282  'input' => [
283  'autocomplete' => false
284  ],
285  'section' => 'actions',
286  ];
287 
288  $a['NamespaceRestrictions'] = [
289  'type' => 'namespacesmultiselect',
290  'label' => $this->msg( 'ipb-namespaces-label' )->text(),
291  'exists' => true,
292  'cssclass' => 'mw-htmlform-checkradio-indent mw-block-partial-restriction',
293  'input' => [
294  'autocomplete' => false
295  ],
296  'section' => 'actions',
297  ];
298 
299  if ( $conf->get( MainConfigNames::EnablePartialActionBlocks ) ) {
300  $blockActions = $this->blockActionInfo->getAllBlockActions();
301  $a['ActionRestrictions'] = [
302  'type' => 'multiselect',
303  'cssclass' => 'mw-htmlform-checkradio-indent mw-block-partial-restriction mw-block-action-restriction',
304  'options-messages' => array_combine(
305  array_map( static function ( $action ) {
306  return "ipb-action-$action";
307  }, array_keys( $blockActions ) ),
308  $blockActions
309  ),
310  'section' => 'actions',
311  ];
312  }
313 
314  $a['CreateAccount'] = [
315  'type' => 'check',
316  'cssclass' => 'mw-block-restriction',
317  'label-message' => 'ipbcreateaccount',
318  'default' => true,
319  'section' => 'details',
320  ];
321 
322  if ( $this->blockPermissionCheckerFactory
323  ->newBlockPermissionChecker( null, $user )
324  ->checkEmailPermissions()
325  ) {
326  $a['DisableEmail'] = [
327  'type' => 'check',
328  'cssclass' => 'mw-block-restriction',
329  'label-message' => 'ipbemailban',
330  'section' => 'details',
331  ];
332  }
333 
334  if ( $blockAllowsUTEdit ) {
335  $a['DisableUTEdit'] = [
336  'type' => 'check',
337  'cssclass' => 'mw-block-restriction',
338  'label-message' => 'ipb-disableusertalk',
339  'default' => false,
340  'section' => 'details',
341  ];
342  }
343 
344  $defaultExpiry = $this->msg( 'ipb-default-expiry' )->inContentLanguage();
345  if ( $this->type === DatabaseBlock::TYPE_RANGE || $this->type === DatabaseBlock::TYPE_IP ) {
346  $defaultExpiryIP = $this->msg( 'ipb-default-expiry-ip' )->inContentLanguage();
347  if ( !$defaultExpiryIP->isDisabled() ) {
348  $defaultExpiry = $defaultExpiryIP;
349  }
350  }
351 
352  $a['Expiry'] = [
353  'type' => 'expiry',
354  'required' => true,
355  'options' => $suggestedDurations,
356  'default' => $defaultExpiry->text(),
357  'section' => 'expiry',
358  ];
359 
360  $a['Reason'] = [
361  'type' => 'selectandother',
362  // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
363  // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
364  // Unicode codepoints.
366  'maxlength-unit' => 'codepoints',
367  'options-message' => 'ipbreason-dropdown',
368  'section' => 'reason',
369  ];
370 
371  $a['AutoBlock'] = [
372  'type' => 'check',
373  'label-message' => [
374  'ipbenableautoblock',
375  Message::durationParam( $conf->get( MainConfigNames::AutoblockExpiry ) )
376  ],
377  'default' => true,
378  'section' => 'options',
379  ];
380 
381  # Allow some users to hide name from block log, blocklist and listusers
382  if ( $this->getAuthority()->isAllowed( 'hideuser' ) ) {
383  $a['HideUser'] = [
384  'type' => 'check',
385  'label-message' => 'ipbhidename',
386  'cssclass' => 'mw-block-hideuser',
387  'section' => 'options',
388  ];
389  }
390 
391  # Watchlist their user page? (Only if user is logged in)
392  if ( $user->isRegistered() ) {
393  $a['Watch'] = [
394  'type' => 'check',
395  'label-message' => 'ipbwatchuser',
396  'section' => 'options',
397  ];
398  }
399 
400  $a['HardBlock'] = [
401  'type' => 'check',
402  'label-message' => 'ipb-hardblock',
403  'default' => false,
404  'section' => 'options',
405  ];
406 
407  # This is basically a copy of the Target field, but the user can't change it, so we
408  # can see if the warnings we maybe showed to the user before still apply
409  $a['PreviousTarget'] = [
410  'type' => 'hidden',
411  'default' => false,
412  ];
413 
414  # We'll turn this into a checkbox if we need to
415  $a['Confirm'] = [
416  'type' => 'hidden',
417  'default' => '',
418  'label-message' => 'ipb-confirm',
419  'cssclass' => 'mw-block-confirm',
420  ];
421 
422  $this->maybeAlterFormDefaults( $a );
423 
424  // Allow extensions to add more fields
425  $this->getHookRunner()->onSpecialBlockModifyFormFields( $this, $a );
426 
427  return $a;
428  }
429 
435  protected function maybeAlterFormDefaults( &$fields ) {
436  # This will be overwritten by request data
437  $fields['Target']['default'] = (string)$this->target;
438 
439  if ( $this->target ) {
440  $status = $this->blockUtils->validateTarget( $this->target );
441  if ( !$status->isOK() ) {
442  $errors = $status->getErrorsArray();
443  $this->preErrors = array_merge( $this->preErrors, $errors );
444  }
445  }
446 
447  # This won't be
448  $fields['PreviousTarget']['default'] = (string)$this->target;
449 
450  $block = DatabaseBlock::newFromTarget( $this->target );
451 
452  // Populate fields if there is a block that is not an autoblock; if it is a range
453  // block, only populate the fields if the range is the same as $this->target
454  if ( $block instanceof DatabaseBlock && $block->getType() !== DatabaseBlock::TYPE_AUTO
455  && ( $this->type != DatabaseBlock::TYPE_RANGE
456  || ( $this->target && $block->isBlocking( $this->target ) ) )
457  ) {
458  $fields['HardBlock']['default'] = $block->isHardblock();
459  $fields['CreateAccount']['default'] = $block->isCreateAccountBlocked();
460  $fields['AutoBlock']['default'] = $block->isAutoblocking();
461 
462  if ( isset( $fields['DisableEmail'] ) ) {
463  $fields['DisableEmail']['default'] = $block->isEmailBlocked();
464  }
465 
466  if ( isset( $fields['HideUser'] ) ) {
467  $fields['HideUser']['default'] = $block->getHideName();
468  }
469 
470  if ( isset( $fields['DisableUTEdit'] ) ) {
471  $fields['DisableUTEdit']['default'] = !$block->isUsertalkEditAllowed();
472  }
473 
474  // If the username was hidden (ipb_deleted == 1), don't show the reason
475  // unless this user also has rights to hideuser: T37839
476  if ( !$block->getHideName() || $this->getAuthority()->isAllowed( 'hideuser' ) ) {
477  $fields['Reason']['default'] = $block->getReasonComment()->text;
478  } else {
479  $fields['Reason']['default'] = '';
480  }
481 
482  if ( $this->getRequest()->wasPosted() ) {
483  # Ok, so we got a POST submission asking us to reblock a user. So show the
484  # confirm checkbox; the user will only see it if they haven't previously
485  $fields['Confirm']['type'] = 'check';
486  } else {
487  # We got a target, but it wasn't a POST request, so the user must have gone
488  # to a link like [[Special:Block/User]]. We don't need to show the checkbox
489  # as long as they go ahead and block *that* user
490  $fields['Confirm']['default'] = 1;
491  }
492 
493  if ( $block->getExpiry() == 'infinity' ) {
494  $fields['Expiry']['default'] = 'infinite';
495  } else {
496  $fields['Expiry']['default'] = wfTimestamp( TS_RFC2822, $block->getExpiry() );
497  }
498 
499  if ( !$block->isSitewide() ) {
500  $fields['EditingRestriction']['default'] = 'partial';
501 
502  $pageRestrictions = [];
503  $namespaceRestrictions = [];
504  foreach ( $block->getRestrictions() as $restriction ) {
505  if ( $restriction instanceof PageRestriction && $restriction->getTitle() ) {
506  $pageRestrictions[] = $restriction->getTitle()->getPrefixedText();
507  } elseif ( $restriction instanceof NamespaceRestriction &&
508  $this->namespaceInfo->exists( $restriction->getValue() )
509  ) {
510  $namespaceRestrictions[] = $restriction->getValue();
511  }
512  }
513 
514  // Sort the restrictions so they are in alphabetical order.
515  sort( $pageRestrictions );
516  $fields['PageRestrictions']['default'] = implode( "\n", $pageRestrictions );
517  sort( $namespaceRestrictions );
518  $fields['NamespaceRestrictions']['default'] = implode( "\n", $namespaceRestrictions );
519 
520  if ( $this->getConfig()->get( MainConfigNames::EnablePartialActionBlocks ) ) {
521  $actionRestrictions = [];
522  foreach ( $block->getRestrictions() as $restriction ) {
523  if ( $restriction instanceof ActionRestriction ) {
524  $actionRestrictions[] = $restriction->getValue();
525  }
526  }
527  $fields['ActionRestrictions']['default'] = $actionRestrictions;
528  }
529  }
530 
531  $this->alreadyBlocked = true;
532  $this->preErrors[] = [ 'ipb-needreblock', wfEscapeWikiText( $block->getTargetName() ) ];
533  }
534 
535  if ( $this->alreadyBlocked || $this->getRequest()->wasPosted()
536  || $this->getRequest()->getCheck( 'wpCreateAccount' )
537  ) {
538  $this->getOutput()->addJsConfigVars( 'wgCreateAccountDirty', true );
539  }
540 
541  # We always need confirmation to do HideUser
542  if ( $this->requestedHideUser ) {
543  $fields['Confirm']['type'] = 'check';
544  unset( $fields['Confirm']['default'] );
545  $this->preErrors[] = [ 'ipb-confirmhideuser', 'ipb-confirmaction' ];
546  }
547 
548  # Or if the user is trying to block themselves
549  if ( (string)$this->target === $this->getUser()->getName() ) {
550  $fields['Confirm']['type'] = 'check';
551  unset( $fields['Confirm']['default'] );
552  $this->preErrors[] = [ 'ipb-blockingself', 'ipb-confirmaction' ];
553  }
554  }
555 
560  protected function preHtml() {
561  $this->getOutput()->addModuleStyles( [
562  'mediawiki.widgets.TagMultiselectWidget.styles',
563  'mediawiki.special',
564  ] );
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 ) ) {
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 
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' ) ) {
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  list( $target, $type ) = $blockUtils->parseBlockTarget( $data['Target'] );
817  if ( $type == DatabaseBlock::TYPE_USER ) {
818  $user = $target;
819  $target = $user->getName();
820  $userId = $user->getId();
821 
822  # Give admins a heads-up before they go and block themselves. Much messier
823  # to do this for IPs, but it's pretty unlikely they'd ever get the 'block'
824  # permission anyway, although the code does allow for it.
825  # Note: Important to use $target instead of $data['Target']
826  # since both $data['PreviousTarget'] and $target are normalized
827  # but $data['target'] gets overridden by (non-normalized) request variable
828  # from previous request.
829  if ( $target === $performer->getUser()->getName() &&
830  ( $data['PreviousTarget'] !== $target || !$data['Confirm'] )
831  ) {
832  return [ 'ipb-blockingself', 'ipb-confirmaction' ];
833  }
834 
835  if ( $data['HideUser'] && !$data['Confirm'] ) {
836  return [ 'ipb-confirmhideuser', 'ipb-confirmaction' ];
837  }
838  } elseif ( $type == DatabaseBlock::TYPE_RANGE ) {
839  $user = null;
840  $userId = 0;
841  } elseif ( $type == DatabaseBlock::TYPE_IP ) {
842  $user = null;
843  $target = $target->getName();
844  $userId = 0;
845  } else {
846  # This should have been caught in the form field validation
847  return [ 'badipaddress' ];
848  }
849 
850  // Reason, to be passed to the block object. For default values of reason, see
851  // HTMLSelectAndOtherField::getDefault
852  $blockReason = $data['Reason'][0] ?? '';
853 
854  $pageRestrictions = [];
855  $namespaceRestrictions = [];
856  $actionRestrictions = [];
857  if ( $isPartialBlock ) {
858  if ( isset( $data['PageRestrictions'] ) && $data['PageRestrictions'] !== '' ) {
859  $titles = explode( "\n", $data['PageRestrictions'] );
860  foreach ( $titles as $title ) {
861  $pageRestrictions[] = PageRestriction::newFromTitle( $title );
862  }
863  }
864  if ( isset( $data['NamespaceRestrictions'] ) && $data['NamespaceRestrictions'] !== '' ) {
865  $namespaceRestrictions = array_map( static function ( $id ) {
866  return new NamespaceRestriction( 0, (int)$id );
867  }, explode( "\n", $data['NamespaceRestrictions'] ) );
868  }
869  if (
870  $enablePartialActionBlocks &&
871  isset( $data['ActionRestrictions'] ) &&
872  $data['ActionRestrictions'] !== ''
873  ) {
874  $actionRestrictions = array_map( static function ( $id ) {
875  return new ActionRestriction( 0, $id );
876  }, $data['ActionRestrictions'] );
877  }
878  }
879  $restrictions = array_merge( $pageRestrictions, $namespaceRestrictions, $actionRestrictions );
880 
881  if ( !isset( $data['Tags'] ) ) {
882  $data['Tags'] = [];
883  }
884 
885  $blockOptions = [
886  'isCreateAccountBlocked' => $data['CreateAccount'],
887  'isHardBlock' => $data['HardBlock'],
888  'isAutoblocking' => $data['AutoBlock'],
889  'isHideUser' => $data['HideUser'],
890  'isPartial' => $isPartialBlock,
891  ];
892 
893  if ( isset( $data['DisableUTEdit'] ) ) {
894  $blockOptions['isUserTalkEditBlocked'] = $data['DisableUTEdit'];
895  }
896  if ( isset( $data['DisableEmail'] ) ) {
897  $blockOptions['isEmailBlocked'] = $data['DisableEmail'];
898  }
899 
900  $blockUser = $blockUserFactory->newBlockUser(
901  $target,
902  $performer,
903  $data['Expiry'],
904  $blockReason,
905  $blockOptions,
906  $restrictions,
907  $data['Tags']
908  );
909 
910  # Indicates whether the user is confirming the block and is aware of
911  # the conflict (did not change the block target in the meantime)
912  $blockNotConfirmed = !$data['Confirm'] || ( array_key_exists( 'PreviousTarget', $data )
913  && $data['PreviousTarget'] !== $target );
914 
915  # Special case for API - T34434
916  $reblockNotAllowed = ( array_key_exists( 'Reblock', $data ) && !$data['Reblock'] );
917 
918  $doReblock = !$blockNotConfirmed && !$reblockNotAllowed;
919 
920  $status = $blockUser->placeBlock( $doReblock );
921  if ( !$status->isOK() ) {
922  return $status;
923  }
924 
925  if (
926  // Can't watch a rangeblock
927  $type != DatabaseBlock::TYPE_RANGE
928 
929  // Technically a wiki can be configured to allow anonymous users to place blocks,
930  // in which case the 'Watch' field isn't included in the form shown, and we should
931  // not try to access it.
932  && array_key_exists( 'Watch', $data )
933  && $data['Watch']
934  ) {
935  MediaWikiServices::getInstance()->getWatchlistManager()->addWatchIgnoringRights(
936  $performer->getUser(),
937  Title::makeTitle( NS_USER, $target )
938  );
939  }
940 
941  return true;
942  }
943 
954  public static function getSuggestedDurations( Language $lang = null, $includeOther = true ) {
955  $msg = $lang === null
956  ? wfMessage( 'ipboptions' )->inContentLanguage()->text()
957  : wfMessage( 'ipboptions' )->inLanguage( $lang )->text();
958 
959  if ( $msg == '-' ) {
960  return [];
961  }
962 
963  $a = XmlSelect::parseOptionsMessage( $msg );
964 
965  if ( $a && $includeOther ) {
966  // if options exist, add other to the end instead of the beginning (which
967  // is what happens by default).
968  $a[ wfMessage( 'ipbother' )->text() ] = 'other';
969  }
970 
971  return $a;
972  }
973 
983  public static function parseExpiryInput( $expiry ) {
984  return BlockUser::parseExpiryInput( $expiry );
985  }
986 
994  public static function canBlockEmail( UserIdentity $user ) {
995  return MediaWikiServices::getInstance()
996  ->getBlockPermissionCheckerFactory()
997  ->newBlockPermissionChecker( null, User::newFromIdentity( $user ) )
998  ->checkEmailPermissions();
999  }
1000 
1015  public static function checkUnblockSelf( $target, Authority $performer ) {
1016  wfDeprecated( __METHOD__, '1.36' );
1017  return MediaWikiServices::getInstance()
1018  ->getBlockPermissionCheckerFactory()
1019  ->newBlockPermissionChecker( $target, $performer )
1020  ->checkBlockPermissions();
1021  }
1022 
1029  public function onSubmit( array $data, HTMLForm $form = null ) {
1030  return self::processFormInternal(
1031  $data,
1032  $this->getAuthority(),
1033  $this->blockUserFactory,
1034  $this->blockUtils
1035  );
1036  }
1037 
1042  public function onSuccess() {
1043  $out = $this->getOutput();
1044  $out->setPageTitle( $this->msg( 'blockipsuccesssub' ) );
1045  $out->addWikiMsg( 'blockipsuccesstext', wfEscapeWikiText( $this->target ) );
1046  }
1047 
1056  public function prefixSearchSubpages( $search, $limit, $offset ) {
1057  $search = $this->userNameUtils->getCanonical( $search );
1058  if ( !$search ) {
1059  // No prefix suggestion for invalid user
1060  return [];
1061  }
1062  // Autocomplete subpage as user list - public to allow caching
1063  return $this->userNamePrefixSearch
1064  ->search( UserNamePrefixSearch::AUDIENCE_PUBLIC, $search, $limit, $offset );
1065  }
1066 
1067  protected function getGroupName() {
1068  return 'users';
1069  }
1070 }
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
const COMMENT_CHARACTER_LIMIT
Maximum length of a comment in UTF-8 characters.
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:943
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
Definition: HTMLForm.php:1604
setSubmitDestructive()
Identify that the submit button in the form has a destructive action.
Definition: HTMLForm.php:1590
addHeaderText( $msg, $section=null)
Add HTML to the header, inside the form.
Definition: HTMLForm.php:929
Marks HTML that shouldn't be escaped.
Definition: HtmlArmor.php:30
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:214
Base class for language-specific code.
Definition: Language.php:53
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
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.
Definition: BlockUtils.php:46
parseBlockTarget( $target)
From an existing block, get the target and the type of target.
Definition: BlockUtils.php:92
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:1177
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.
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.
array $preErrors
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.
bool $alreadyBlocked
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.
getUser()
Shortcut to get the User executing 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.
getAuthority()
Shortcut to get the Authority executing this instance.
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.
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:638
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
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...
Definition: WebRequest.php:44
static parseOptionsMessage(string $msg)
Parse labels and values out of a comma- and colon-separated list of options, such as is used for expi...
Definition: XmlSelect.php:146
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