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 
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();
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  [ $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.
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 ) ) {
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 
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' ) ) {
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  [ $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 
967  $a = XmlSelect::parseOptionsMessage( $msg );
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 }
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:952
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
Definition: HTMLForm.php:1613
setSubmitDestructive()
Identify that the submit button in the form has a destructive action.
Definition: HTMLForm.php:1599
addHeaderText( $msg, $section=null)
Add HTML to the header, inside the form.
Definition: HTMLForm.php:938
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:54
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:1155
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.
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:641
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:664
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:47
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