MediaWiki master
SpecialUnblock.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Specials;
22
40
47
49 protected $target;
50
52 protected $block;
53
54 private UnblockUserFactory $unblockUserFactory;
55 private BlockTargetFactory $blockTargetFactory;
56 private DatabaseBlockStore $blockStore;
57 private UserNameUtils $userNameUtils;
58 private UserNamePrefixSearch $userNamePrefixSearch;
59 private WatchlistManager $watchlistManager;
60
61 protected bool $useCodex = false;
62
63 public function __construct(
64 UnblockUserFactory $unblockUserFactory,
65 BlockTargetFactory $blockTargetFactory,
66 DatabaseBlockStore $blockStore,
67 UserNameUtils $userNameUtils,
68 UserNamePrefixSearch $userNamePrefixSearch,
69 WatchlistManager $watchlistManager
70 ) {
71 parent::__construct( 'Unblock', 'block' );
72 $this->unblockUserFactory = $unblockUserFactory;
73 $this->blockTargetFactory = $blockTargetFactory;
74 $this->blockStore = $blockStore;
75 $this->userNameUtils = $userNameUtils;
76 $this->userNamePrefixSearch = $userNamePrefixSearch;
77 $this->watchlistManager = $watchlistManager;
78 $this->useCodex = $this->getConfig()->get( MainConfigNames::UseCodexSpecialBlock ) ||
79 $this->getRequest()->getBool( 'usecodex' );
80 }
81
82 public function doesWrites() {
83 return true;
84 }
85
86 public function execute( $par ) {
87 $this->checkPermissions();
88 $this->checkReadOnly();
89
90 $this->target = $this->getTargetFromRequest( $par, $this->getRequest() );
91
92 // T382539
93 if ( $this->useCodex ) {
94 // If target is null, redirect to Special:Block
95 if ( $this->target === null ) {
96 // Use 301 (Moved Permanently) as this is a deprecation
97 $this->getOutput()->redirect(
98 SpecialPage::getTitleFor( 'Block' )->getFullURL( 'redirected=1' ),
99 '301'
100 );
101 return;
102 }
103 }
104
105 $this->block = $this->blockStore->newFromTarget( $this->target );
106 if ( $this->target instanceof BlockTargetWithUserPage ) {
107 // Set the 'relevant user' in the skin, so it displays links like Contributions,
108 // User logs, UserRights, etc.
109 $this->getSkin()->setRelevantUser( $this->target->getUserIdentity() );
110 }
111
112 $this->setHeaders();
113 $this->outputHeader();
114 $this->addHelpLink( 'Help:Blocking users' );
115
116 $out = $this->getOutput();
117 $out->setPageTitleMsg( $this->msg( 'unblock-target' ) );
118 $out->addModules( [ 'mediawiki.userSuggest', 'mediawiki.special.block' ] );
119
120 $form = HTMLForm::factory( 'ooui', $this->getFields(), $this->getContext() )
121 ->setWrapperLegendMsg( 'unblock-target' )
122 ->setSubmitCallback( function ( array $data, HTMLForm $form ) {
123 if ( $this->target instanceof BlockTargetWithUserPage && $data['Watch'] ) {
124 $this->watchlistManager->addWatchIgnoringRights(
125 $form->getUser(),
126 Title::newFromPageReference( $this->target->getUserPage() )
127 );
128 }
129 $status = $this->unblockUserFactory->newUnblockUser(
130 $this->target,
131 $form->getContext()->getAuthority(),
132 $data['Reason'],
133 $data['Tags'] ?? []
134 )->unblock();
135
136 if ( $status->hasMessage( 'ipb_cant_unblock_multiple_blocks' ) ) {
137 // Add additional message sending users to [[Special:Block/Username]]
138 $status->error( 'unblock-error-multiblocks', $this->target );
139 }
140 return $status;
141 } )
142 ->setSubmitTextMsg( 'ipusubmit' )
143 ->addPreHtml( $this->msg( 'unblockiptext' )->parseAsBlock() );
144
145 if ( $this->target ) {
146 $userPage = $this->target->getLogPage();
147 $targetName = (string)$this->target;
148 // Get relevant extracts from the block and suppression logs, if possible
149 $logExtract = '';
150 LogEventsList::showLogExtract(
151 $logExtract,
152 'block',
153 $userPage,
154 '',
155 [
156 'lim' => 10,
157 'msgKey' => [
158 'unblocklog-showlog',
159 $targetName,
160 ],
161 'showIfEmpty' => false
162 ]
163 );
164 if ( $logExtract !== '' ) {
165 $form->addPostHtml( $logExtract );
166 }
167
168 // Add suppression block entries if allowed
169 if ( $this->getAuthority()->isAllowed( 'suppressionlog' ) ) {
170 $logExtract = '';
171 LogEventsList::showLogExtract(
172 $logExtract,
173 'suppress',
174 $userPage,
175 '',
176 [
177 'lim' => 10,
178 'conds' => [ 'log_action' => [ 'block', 'reblock', 'unblock' ] ],
179 'msgKey' => [
180 'unblocklog-showsuppresslog',
181 $targetName,
182 ],
183 'showIfEmpty' => false
184 ]
185 );
186 if ( $logExtract !== '' ) {
187 $form->addPostHtml( $logExtract );
188 }
189 }
190 }
191
192 if ( $form->show() ) {
193 $msgsByType = [
194 Block::TYPE_IP => 'unblocked-ip',
195 Block::TYPE_USER => 'unblocked',
196 Block::TYPE_RANGE => 'unblocked-range',
197 Block::TYPE_AUTO => 'unblocked-id'
198 ];
199 $out->addWikiMsg(
200 $msgsByType[$this->target->getType()],
201 wfEscapeWikiText( (string)$this->target )
202 );
203 }
204 }
205
215 private function getTargetFromRequest( ?string $par, WebRequest $request ) {
216 $possibleTargets = [
217 $request->getVal( 'wpTarget', null ),
218 $par,
219 $request->getVal( 'ip', null ),
220 // B/C @since 1.18
221 $request->getVal( 'wpBlockAddress', null ),
222 ];
223 foreach ( $possibleTargets as $possibleTarget ) {
224 $target = $this->blockTargetFactory->newFromString( $possibleTarget );
225 // If type is not null then target is valid
226 if ( $target ) {
227 break;
228 }
229 }
230 return $target;
231 }
232
233 protected function getFields() {
234 $fields = [
235 'Target' => [
236 'type' => 'text',
237 'label-message' => 'unblock-target-label',
238 'autofocus' => true,
239 'size' => '45',
240 'required' => true,
241 'cssclass' => 'mw-autocomplete-user', // used by mediawiki.userSuggest
242 ],
243 'Name' => [
244 'type' => 'info',
245 'label-message' => 'unblock-target-label',
246 ],
247 'Reason' => [
248 'type' => 'text',
249 'label-message' => 'ipbreason',
250 ]
251 ];
252
253 if ( $this->block instanceof Block ) {
254 $type = $this->block->getType();
255 $targetName = $this->block->getTargetName();
256
257 // Autoblocks are logged as "autoblock #123 because the IP was recently used by
258 // User:Foo, and we've just got any block, auto or not, that applies to a target
259 // the user has specified. Someone could be fishing to connect IPs to autoblocks,
260 // so don't show any distinction between unblocked IPs and autoblocked IPs
261 if ( $type == Block::TYPE_AUTO && $this->target->getType() == Block::TYPE_IP ) {
262 $fields['Target']['default'] = (string)$this->target;
263 unset( $fields['Name'] );
264 } else {
265 $fields['Target']['default'] = $targetName;
266 $fields['Target']['type'] = 'hidden';
267 switch ( $type ) {
268 case Block::TYPE_IP:
269 $fields['Name']['default'] = $this->getLinkRenderer()->makeKnownLink(
270 $this->getSpecialPageFactory()->getTitleForAlias( 'Contributions/' . $targetName ),
271 $targetName
272 );
273 $fields['Name']['raw'] = true;
274 break;
275 case Block::TYPE_USER:
276 $fields['Name']['default'] = $this->getLinkRenderer()->makeLink(
277 new TitleValue( NS_USER, $targetName ),
278 $targetName
279 );
280 $fields['Name']['raw'] = true;
281 break;
282
283 case Block::TYPE_RANGE:
284 $fields['Name']['default'] = $targetName;
285 break;
286
287 case Block::TYPE_AUTO:
288 // Don't expose the real target of the autoblock
289 $fields['Name']['default'] = $this->block->getRedactedName();
290 $fields['Name']['raw'] = true;
291 $fields['Target']['default'] = $this->block->getRedactedTarget()->toString();
292 break;
293 }
294 // Target is hidden, so the reason is the first element
295 $fields['Target']['autofocus'] = false;
296 $fields['Reason']['autofocus'] = true;
297 }
298 } else {
299 $fields['Target']['default'] = $this->target;
300 unset( $fields['Name'] );
301 }
302 // Watchlist their user page? (Only if user is logged in)
303 if ( $this->getUser()->isRegistered() ) {
304 $fields['Watch'] = [
305 'type' => 'check',
306 'label-message' => 'ipbwatchuser',
307 ];
308 }
309
310 return $fields;
311 }
312
321 public function prefixSearchSubpages( $search, $limit, $offset ) {
322 $search = $this->userNameUtils->getCanonical( $search );
323 if ( !$search ) {
324 // No prefix suggestion for invalid user
325 return [];
326 }
327 // Autocomplete subpage as user list - public to allow caching
328 return $this->userNamePrefixSearch
329 ->search( UserNamePrefixSearch::AUDIENCE_PUBLIC, $search, $limit, $offset );
330 }
331
332 protected function getGroupName() {
333 return 'users';
334 }
335}
336
341class_alias( SpecialUnblock::class, 'SpecialUnblock' );
const NS_USER
Definition Defines.php:67
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Factory for BlockTarget objects.
Base class for block targets.
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
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:210
show()
The here's-one-I-made-earlier option: do the submission if posted, or display the form with or withou...
Definition HTMLForm.php:720
addPostHtml( $html)
Add HTML to the end of the display.
A class containing constants representing the names of configuration variables.
const UseCodexSpecialBlock
Name constant for the UseCodexSpecialBlock setting, for use with Config::get()
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form,...
getVal( $name, $default=null)
Fetch a text string from this web request's $_GET, $_POST or path router vars and partially normalize...
Parent class for all special pages.
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.
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,...
getUser()
Shortcut to get the User executing this instance.
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
getConfig()
Shortcut to get main config object.
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 By default the message key is the canonical name of...
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
A special page for unblocking users.
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 POST requests to this special page require write access to the wiki.
__construct(UnblockUserFactory $unblockUserFactory, BlockTargetFactory $blockTargetFactory, DatabaseBlockStore $blockStore, 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.
Represents a title within MediaWiki.
Definition Title.php:78
Handles searching prefixes of user names.
UserNameUtils service.
Shared interface for user and single IP targets, that is, for targets with a meaningful user page lin...
Represents a block that may prevent users from performing specific operations.
Definition Block.php:45