MediaWiki  master
SpecialUnblock.php
Go to the documentation of this file.
1 <?php
24 namespace MediaWiki\Specials;
25 
26 use HTMLForm;
27 use LogEventsList;
39 use Wikimedia\IPUtils;
40 
46 class SpecialUnblock extends SpecialPage {
47 
49  protected $target;
50 
52  protected $type;
53 
54  protected $block;
55 
56  private UnblockUserFactory $unblockUserFactory;
57  private BlockUtils $blockUtils;
58  private UserNameUtils $userNameUtils;
59  private UserNamePrefixSearch $userNamePrefixSearch;
60  private WatchlistManager $watchlistManager;
61 
69  public function __construct(
70  UnblockUserFactory $unblockUserFactory,
71  BlockUtils $blockUtils,
72  UserNameUtils $userNameUtils,
73  UserNamePrefixSearch $userNamePrefixSearch,
74  WatchlistManager $watchlistManager
75  ) {
76  parent::__construct( 'Unblock', 'block' );
77  $this->unblockUserFactory = $unblockUserFactory;
78  $this->blockUtils = $blockUtils;
79  $this->userNameUtils = $userNameUtils;
80  $this->userNamePrefixSearch = $userNamePrefixSearch;
81  $this->watchlistManager = $watchlistManager;
82  }
83 
84  public function doesWrites() {
85  return true;
86  }
87 
88  public function execute( $par ) {
89  $this->checkPermissions();
90  $this->checkReadOnly();
91 
92  [ $this->target, $this->type ] = $this->getTargetAndType( $par, $this->getRequest() );
93  $this->block = DatabaseBlock::newFromTarget( $this->target );
94  if ( $this->target instanceof UserIdentity ) {
95  // Set the 'relevant user' in the skin, so it displays links like Contributions,
96  // User logs, UserRights, etc.
97  $this->getSkin()->setRelevantUser( $this->target );
98  }
99 
100  $this->setHeaders();
101  $this->outputHeader();
102  $this->addHelpLink( 'Help:Blocking users' );
103 
104  $out = $this->getOutput();
105  $out->setPageTitleMsg( $this->msg( 'unblockip' ) );
106  $out->addModules( [ 'mediawiki.userSuggest', 'mediawiki.special.block' ] );
107 
108  $form = HTMLForm::factory( 'ooui', $this->getFields(), $this->getContext() )
109  ->setWrapperLegendMsg( 'unblockip' )
110  ->setSubmitCallback( function ( array $data, HTMLForm $form ) {
111  if ( $this->type != DatabaseBlock::TYPE_RANGE
112  && $this->type != DatabaseBlock::TYPE_AUTO
113  && $data['Watch']
114  ) {
115  $this->watchlistManager->addWatchIgnoringRights(
116  $form->getUser(),
117  Title::makeTitle( NS_USER, $this->target )
118  );
119  }
120  return $this->unblockUserFactory->newUnblockUser(
121  $data['Target'],
122  $form->getContext()->getAuthority(),
123  $data['Reason'],
124  $data['Tags'] ?? []
125  )->unblock();
126  } )
127  ->setSubmitTextMsg( 'ipusubmit' )
128  ->addPreHtml( $this->msg( 'unblockiptext' )->parseAsBlock() );
129 
130  $userPage = $this->getTargetUserTitle( $this->target );
131  if ( $userPage ) {
132  // Get relevant extracts from the block and suppression logs, if possible
133  $logExtract = '';
135  $logExtract,
136  'block',
137  $userPage,
138  '',
139  [
140  'lim' => 10,
141  'msgKey' => [
142  'unblocklog-showlog',
143  $userPage->getText(),
144  ],
145  'showIfEmpty' => false
146  ]
147  );
148  if ( $logExtract !== '' ) {
149  $form->addPostHtml( $logExtract );
150  }
151 
152  // Add suppression block entries if allowed
153  if ( $this->getAuthority()->isAllowed( 'suppressionlog' ) ) {
154  $logExtract = '';
156  $logExtract,
157  'suppress',
158  $userPage,
159  '',
160  [
161  'lim' => 10,
162  'conds' => [ 'log_action' => [ 'block', 'reblock', 'unblock' ] ],
163  'msgKey' => [
164  'unblocklog-showsuppresslog',
165  $userPage->getText(),
166  ],
167  'showIfEmpty' => false
168  ]
169  );
170  if ( $logExtract !== '' ) {
171  $form->addPostHtml( $logExtract );
172  }
173  }
174  }
175 
176  if ( $form->show() ) {
177  switch ( $this->type ) {
179  // @phan-suppress-next-line PhanTypeMismatchArgumentNullable target is set when type is set
180  $out->addWikiMsg( 'unblocked-ip', wfEscapeWikiText( $this->target ) );
181  break;
183  // @phan-suppress-next-line PhanTypeMismatchArgumentNullable target is set when type is set
184  $out->addWikiMsg( 'unblocked', wfEscapeWikiText( $this->target ) );
185  break;
187  // @phan-suppress-next-line PhanTypeMismatchArgumentNullable target is set when type is set
188  $out->addWikiMsg( 'unblocked-range', wfEscapeWikiText( $this->target ) );
189  break;
192  // @phan-suppress-next-line PhanTypeMismatchArgumentNullable target is set when type is set
193  $out->addWikiMsg( 'unblocked-id', wfEscapeWikiText( $this->target ) );
194  break;
195  }
196  }
197  }
198 
209  private function getTargetAndType( ?string $par, WebRequest $request ) {
210  $possibleTargets = [
211  $request->getVal( 'wpTarget', null ),
212  $par,
213  $request->getVal( 'ip', null ),
214  // B/C @since 1.18
215  $request->getVal( 'wpBlockAddress', null ),
216  ];
217  foreach ( $possibleTargets as $possibleTarget ) {
218  $targetAndType = $this->blockUtils->parseBlockTarget( $possibleTarget );
219  // If type is not null then target is valid
220  if ( $targetAndType[ 1 ] !== null ) {
221  break;
222  }
223  }
224  return $targetAndType;
225  }
226 
233  private function getTargetUserTitle( $target ): ?Title {
234  if ( $target instanceof UserIdentity ) {
235  return Title::makeTitle( NS_USER, $target->getName() );
236  }
237 
238  if ( is_string( $target ) && IPUtils::isIPAddress( $target ) ) {
239  return Title::makeTitle( NS_USER, $target );
240  }
241 
242  return null;
243  }
244 
245  protected function getFields() {
246  $fields = [
247  'Target' => [
248  'type' => 'text',
249  'label-message' => 'ipaddressorusername',
250  'autofocus' => true,
251  'size' => '45',
252  'required' => true,
253  'cssclass' => 'mw-autocomplete-user', // used by mediawiki.userSuggest
254  ],
255  'Name' => [
256  'type' => 'info',
257  'label-message' => 'ipaddressorusername',
258  ],
259  'Reason' => [
260  'type' => 'text',
261  'label-message' => 'ipbreason',
262  ]
263  ];
264 
265  if ( $this->block instanceof DatabaseBlock ) {
266  $type = $this->block->getType();
267  $targetName = $this->block->getTargetName();
268 
269  // Autoblocks are logged as "autoblock #123 because the IP was recently used by
270  // User:Foo, and we've just got any block, auto or not, that applies to a target
271  // the user has specified. Someone could be fishing to connect IPs to autoblocks,
272  // so don't show any distinction between unblocked IPs and autoblocked IPs
273  if ( $type == DatabaseBlock::TYPE_AUTO && $this->type == DatabaseBlock::TYPE_IP ) {
274  $fields['Target']['default'] = $this->target;
275  unset( $fields['Name'] );
276  } else {
277  $fields['Target']['default'] = $targetName;
278  $fields['Target']['type'] = 'hidden';
279  switch ( $type ) {
281  $fields['Name']['default'] = $this->getLinkRenderer()->makeKnownLink(
282  $this->getSpecialPageFactory()->getTitleForAlias( 'Contributions/' . $targetName ),
283  $targetName
284  );
285  $fields['Name']['raw'] = true;
286  break;
288  $fields['Name']['default'] = $this->getLinkRenderer()->makeLink(
289  new TitleValue( NS_USER, $targetName ),
290  $targetName
291  );
292  $fields['Name']['raw'] = true;
293  break;
294 
296  $fields['Name']['default'] = $targetName;
297  break;
298 
300  $fields['Name']['default'] = $this->block->getRedactedName();
301  $fields['Name']['raw'] = true;
302  // Don't expose the real target of the autoblock
303  $fields['Target']['default'] = "#{$this->target}";
304  break;
305  }
306  // Target is hidden, so the reason is the first element
307  $fields['Target']['autofocus'] = false;
308  $fields['Reason']['autofocus'] = true;
309  }
310  } else {
311  $fields['Target']['default'] = $this->target;
312  unset( $fields['Name'] );
313  }
314  // Watchlist their user page? (Only if user is logged in)
315  if ( $this->getUser()->isRegistered() ) {
316  $fields['Watch'] = [
317  'type' => 'check',
318  'label-message' => 'ipbwatchuser',
319  ];
320  }
321 
322  return $fields;
323  }
324 
333  public function prefixSearchSubpages( $search, $limit, $offset ) {
334  $search = $this->userNameUtils->getCanonical( $search );
335  if ( !$search ) {
336  // No prefix suggestion for invalid user
337  return [];
338  }
339  // Autocomplete subpage as user list - public to allow caching
340  return $this->userNamePrefixSearch
341  ->search( UserNamePrefixSearch::AUDIENCE_PUBLIC, $search, $limit, $offset );
342  }
343 
344  protected function getGroupName() {
345  return 'users';
346  }
347 }
348 
353 class_alias( SpecialUnblock::class, 'SpecialUnblock' );
getUser()
const NS_USER
Definition: Defines.php:66
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
if(!defined('MW_SETUP_CALLBACK'))
Definition: WebStart.php:88
getContext()
Get the base IContextSource object.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition: HTMLForm.php:158
addPostHtml( $html)
Add HTML to the end of the display.
Definition: HTMLForm.php:1081
show()
The here's-one-I-made-earlier option: do the submission if posted, or display the form with or withou...
Definition: HTMLForm.php:644
static factory( $displayFormat, $descriptor, IContextSource $context, $messagePrefix='')
Construct a HTMLForm object for given display type.
Definition: HTMLForm.php:360
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Backend class for blocking utils.
Definition: BlockUtils.php:46
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
static newFromTarget( $specificTarget, $vagueTarget=null, $fromPrimary=false)
Given a target and the target's type, get an existing block object if possible.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:50
getVal( $name, $default=null)
Fetch a text string and partially normalize it.
Definition: WebRequest.php:507
Parent class for all special pages.
Definition: SpecialPage.php:65
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getSkin()
Shortcut to get the skin being used for this instance.
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
getContext()
Gets the context this SpecialPage is executed in.
getRequest()
Get the WebRequest being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
getAuthority()
Shortcut to get the Authority executing this instance.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
A special page for unblocking users.
UserIdentity string null $target
execute( $par)
Default execute method Checks user permissions.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
doesWrites()
Indicates whether this special page may perform database writes.
int null $type
DatabaseBlock::TYPE_ constant.
__construct(UnblockUserFactory $unblockUserFactory, BlockUtils $blockUtils, UserNameUtils $userNameUtils, UserNamePrefixSearch $userNamePrefixSearch, WatchlistManager $watchlistManager)
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
Represents the target of a wiki link.
Definition: TitleValue.php:44
Represents a title within MediaWiki.
Definition: Title.php:76
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:624
Handles searching prefixes of user names.
UserNameUtils service.
Interface for objects representing user identity.