Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F2753240
SpecialInvestigateBlock.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
SpecialInvestigateBlock.php
View Options
<?php
namespace
MediaWiki\CheckUser\Investigate
;
use
Exception
;
use
MediaWiki\Api\ApiMain
;
use
MediaWiki\Block\BlockPermissionCheckerFactory
;
use
MediaWiki\Block\BlockUserFactory
;
use
MediaWiki\CheckUser\Investigate\Utilities\EventLogger
;
use
MediaWiki\HTMLForm\HTMLForm
;
use
MediaWiki\Linker\Linker
;
use
MediaWiki\MainConfigNames
;
use
MediaWiki\Permissions\PermissionManager
;
use
MediaWiki\Request\DerivativeRequest
;
use
MediaWiki\SpecialPage\FormSpecialPage
;
use
MediaWiki\Title\TitleFormatter
;
use
MediaWiki\Title\TitleValue
;
use
MediaWiki\User\User
;
use
MediaWiki\User\UserFactory
;
use
MediaWiki\User\UserNameUtils
;
use
OOUI\FieldLayout
;
use
OOUI\Widget
;
use
PermissionsError
;
use
Wikimedia\IPUtils
;
class
SpecialInvestigateBlock
extends
FormSpecialPage
{
private
BlockUserFactory
$blockUserFactory
;
private
BlockPermissionCheckerFactory
$blockPermissionCheckerFactory
;
private
PermissionManager
$permissionManager
;
private
TitleFormatter
$titleFormatter
;
private
UserFactory
$userFactory
;
private
EventLogger
$eventLogger
;
private
array
$blockedUsers
=
[];
private
bool
$noticesFailed
=
false
;
/**
* @param BlockUserFactory $blockUserFactory
* @param BlockPermissionCheckerFactory $blockPermissionCheckerFactory
* @param PermissionManager $permissionManager
* @param TitleFormatter $titleFormatter
* @param UserFactory $userFactory
* @param EventLogger $eventLogger
*/
public
function
__construct
(
BlockUserFactory
$blockUserFactory
,
BlockPermissionCheckerFactory
$blockPermissionCheckerFactory
,
PermissionManager
$permissionManager
,
TitleFormatter
$titleFormatter
,
UserFactory
$userFactory
,
EventLogger
$eventLogger
)
{
parent
::
__construct
(
'InvestigateBlock'
,
'checkuser'
);
$this
->
blockUserFactory
=
$blockUserFactory
;
$this
->
blockPermissionCheckerFactory
=
$blockPermissionCheckerFactory
;
$this
->
permissionManager
=
$permissionManager
;
$this
->
titleFormatter
=
$titleFormatter
;
$this
->
userFactory
=
$userFactory
;
$this
->
eventLogger
=
$eventLogger
;
}
/**
* @inheritDoc
*/
public
function
userCanExecute
(
User
$user
)
{
return
parent
::
userCanExecute
(
$user
)
&&
$this
->
permissionManager
->
userHasRight
(
$user
,
'block'
);
}
/**
* @inheritDoc
*/
public
function
checkPermissions
()
{
$user
=
$this
->
getUser
();
if
(
!
parent
::
userCanExecute
(
$user
)
)
{
$this
->
displayRestrictionError
();
}
// User is a checkuser, but now to check for if they can block.
if
(
!
$this
->
permissionManager
->
userHasRight
(
$user
,
'block'
)
)
{
throw
new
PermissionsError
(
'block'
);
}
}
/**
* @inheritDoc
*/
protected
function
getDisplayFormat
()
{
return
'ooui'
;
}
/**
* @inheritDoc
*/
public
function
getFormFields
()
{
$this
->
getOutput
()->
addModules
(
[
'ext.checkUser'
]
);
$this
->
getOutput
()->
addModuleStyles
(
[
'mediawiki.widgets.TagMultiselectWidget.styles'
,
'ext.checkUser.styles'
,
]
);
$this
->
getOutput
()->
enableOOUI
();
$fields
=
[];
$maxBlocks
=
$this
->
getConfig
()->
get
(
'CheckUserMaxBlocks'
);
$fields
[
'Targets'
]
=
[
'type'
=>
'usersmultiselect'
,
'ipallowed'
=>
true
,
'iprange'
=>
true
,
'autofocus'
=>
true
,
'required'
=>
true
,
'exists'
=>
true
,
'input'
=>
[
'autocomplete'
=>
false
,
],
// The following message key is generated:
// * checkuser-investigateblock-target
'section'
=>
'target'
,
'default'
=>
''
,
'max'
=>
$maxBlocks
,
// Show a warning message to the user if the user loaded Special:InvestigateBlock via some kind of
// pre-filled link, and the number of users provided exceeds the limit. This warning is displayed
// elsewhere as an error if the form is submitted.
'filter-callback'
=>
function
(
$users
,
$_
,
?
HTMLForm
$htmlForm
)
use
(
$maxBlocks
)
{
if
(
$users
!==
null
&&
$htmlForm
!==
null
&&
// If wpEditToken is set, then the user is attempting to submit the form and this will be
// shown as an error instead of a warning by HTMLForm.
!
$this
->
getRequest
()->
getVal
(
'wpEditToken'
)
&&
count
(
explode
(
"
\n
"
,
$users
)
)
>
$maxBlocks
)
{
// Show a warning message if the number of users provided exceeds the limit.
$htmlForm
->
addHeaderHtml
(
new
FieldLayout
(
new
Widget
(
[]
),
[
'classes'
=>
[
'mw-htmlform-ooui-header-warnings'
],
'warnings'
=>
[
$this
->
msg
(
'checkuser-investigateblock-warning-users-truncated'
,
$maxBlocks
)->
parse
()
],
]
)
);
}
return
$users
;
},
];
if
(
$this
->
blockPermissionCheckerFactory
->
newBlockPermissionChecker
(
null
,
$this
->
getUser
()
)
->
checkEmailPermissions
()
)
{
$fields
[
'DisableEmail'
]
=
[
'type'
=>
'check'
,
'label-message'
=>
'checkuser-investigateblock-email-label'
,
'default'
=>
false
,
'section'
=>
'actions'
,
];
}
if
(
$this
->
getConfig
()->
get
(
MainConfigNames
::
BlockAllowsUTEdit
)
)
{
$fields
[
'DisableUTEdit'
]
=
[
'type'
=>
'check'
,
'label-message'
=>
'checkuser-investigateblock-usertalk-label'
,
'default'
=>
false
,
'section'
=>
'actions'
,
];
}
$fields
[
'Reblock'
]
=
[
'type'
=>
'check'
,
'label-message'
=>
'checkuser-investigateblock-reblock-label'
,
'default'
=>
false
,
// The following message key is generated:
// * checkuser-investigateblock-actions
'section'
=>
'actions'
,
];
$fields
[
'Reason'
]
=
[
'type'
=>
'selectandother'
,
'options-message'
=>
'checkuser-block-reason-dropdown'
,
'maxlength'
=>
150
,
'required'
=>
true
,
'autocomplete'
=>
false
,
// The following message key is generated:
// * checkuser-investigateblock-reason
'section'
=>
'reason'
,
];
$pageNoticeClass
=
'ext-checkuser-investigate-block-notice'
;
$pageNoticePosition
=
[
'type'
=>
'select'
,
'cssclass'
=>
$pageNoticeClass
,
'label-message'
=>
'checkuser-investigateblock-notice-position-label'
,
'options-messages'
=>
[
'checkuser-investigateblock-notice-prepend'
=>
'prependtext'
,
'checkuser-investigateblock-notice-replace'
=>
'text'
,
'checkuser-investigateblock-notice-append'
=>
'appendtext'
,
],
// The following message key is generated:
// * checkuser-investigateblock-options
'section'
=>
'options'
,
];
$pageNoticeText
=
[
'type'
=>
'text'
,
'cssclass'
=>
$pageNoticeClass
,
'label-message'
=>
'checkuser-investigateblock-notice-text-label'
,
'default'
=>
''
,
'section'
=>
'options'
,
];
$fields
[
'UserPageNotice'
]
=
[
'type'
=>
'check'
,
'label-message'
=>
'checkuser-investigateblock-notice-user-page-label'
,
'default'
=>
false
,
'section'
=>
'options'
,
];
$fields
[
'UserPageNoticePosition'
]
=
array_merge
(
$pageNoticePosition
,
[
'default'
=>
'prependtext'
]
);
$fields
[
'UserPageNoticeText'
]
=
$pageNoticeText
;
$fields
[
'TalkPageNotice'
]
=
[
'type'
=>
'check'
,
'label-message'
=>
'checkuser-investigateblock-notice-talk-page-label'
,
'default'
=>
false
,
'section'
=>
'options'
,
];
$fields
[
'TalkPageNoticePosition'
]
=
array_merge
(
$pageNoticePosition
,
[
'default'
=>
'appendtext'
]
);
$fields
[
'TalkPageNoticeText'
]
=
$pageNoticeText
;
$fields
[
'Confirm'
]
=
[
'type'
=>
$this
->
showConfirmationCheckbox
()
?
'check'
:
'hidden'
,
'default'
=>
''
,
'label-message'
=>
'checkuser-investigateblock-confirm-blocks-label'
,
'cssclass'
=>
'ext-checkuser-investigateblock-block-confirm'
,
];
return
$fields
;
}
/**
* Should the 'Confirm blocks' checkbox be shown?
*
* @return bool True if the form was submitted and the targets input has both IPs and users. Otherwise false.
*/
private
function
showConfirmationCheckbox
():
bool
{
// We cannot access HTMLForm->mWasSubmitted directly to work out if the form was submitted, as this has not
// been generated yet. However, we can approximate this by checking if the request was POSTed and if the
// wpEditToken is set.
return
$this
->
getRequest
()->
wasPosted
()
&&
$this
->
getRequest
()->
getVal
(
'wpEditToken'
)
&&
$this
->
checkForIPsAndUsersInTargetsParam
(
$this
->
getRequest
()->
getText
(
'wpTargets'
)
);
}
/**
* Returns whether the 'Targets' parameter contains both IPs and usernames.
*
* @param string $targets The value of the 'Targets' parameter, either from the request via ::getText or (if in
* ::onSubmit) from the data array.
* @return bool True if the 'Targets' parameter contains both IPs and usernames, false otherwise.
*/
private
function
checkForIPsAndUsersInTargetsParam
(
string
$targets
):
bool
{
// The 'usersmultiselect' field data is formatted by each username being seperated by a newline (\n).
$targets
=
explode
(
"
\n
"
,
$targets
);
// Get an array of booleans indicating whether each target is an IP address. If the array contains both true and
// false, then the 'Targets' parameter contains both IPs and usernames. Otherwise it does not.
$areTargetsIPs
=
array_map
(
[
IPUtils
::
class
,
'isIPAddress'
],
$targets
);
return
in_array
(
true
,
$areTargetsIPs
,
true
)
&&
in_array
(
false
,
$areTargetsIPs
,
true
);
}
/**
* @inheritDoc
*/
public
function
getDescription
()
{
return
$this
->
msg
(
'checkuser-investigateblock'
);
}
/**
* @inheritDoc
*/
protected
function
getMessagePrefix
()
{
return
'checkuser-'
.
strtolower
(
$this
->
getName
()
);
}
/**
* @inheritDoc
*/
protected
function
getGroupName
()
{
return
'users'
;
}
/**
* @inheritDoc
*/
public
function
onSubmit
(
array
$data
)
{
$this
->
blockedUsers
=
[];
// This might have been a hidden field or a checkbox, so interesting data can come from it. This handling is
// copied from SpecialBlock::processFormInternal.
$data
[
'Confirm'
]
=
!
in_array
(
$data
[
'Confirm'
],
[
''
,
'0'
,
null
,
false
],
true
);
// If the targets are both IPs and usernames, we should warn the CheckUser before allowing them to proceed to
// avoid inadvertently violating any privacy policies.
if
(
$this
->
checkForIPsAndUsersInTargetsParam
(
$data
[
'Targets'
]
)
&&
!
$data
[
'Confirm'
]
)
{
return
[
'checkuser-investigateblock-warning-ips-and-users-in-targets'
,
'checkuser-investigateblock-warning-confirmaction'
];
}
$targets
=
explode
(
"
\n
"
,
$data
[
'Targets'
]
);
// Format of $data['Reason'] is an array with items as documented in
// HTMLSelectAndOtherField::loadDataFromRequest. The value in this should not be empty, as the field is marked
// as required and as such the validation will be done by HTMLForm.
$reason
=
$data
[
'Reason'
][
0
];
foreach
(
$targets
as
$target
)
{
$isIP
=
IPUtils
::
isIPAddress
(
$target
);
if
(
!
$isIP
)
{
$user
=
$this
->
userFactory
->
newFromName
(
$target
);
if
(
!
$user
||
!
$user
->
getId
()
)
{
continue
;
}
}
$expiry
=
$isIP
?
'1 week'
:
'indefinite'
;
$status
=
$this
->
blockUserFactory
->
newBlockUser
(
$target
,
$this
->
getUser
(),
$expiry
,
$reason
,
[
'isHardBlock'
=>
!
$isIP
,
'isCreateAccountBlocked'
=>
true
,
'isAutoblocking'
=>
true
,
'isEmailBlocked'
=>
$data
[
'DisableEmail'
]
??
false
,
'isUserTalkEditBlocked'
=>
$data
[
'DisableUTEdit'
]
??
false
,
]
)->
placeBlock
(
$data
[
'Reblock'
]
);
if
(
$status
->
isOK
()
)
{
$this
->
blockedUsers
[]
=
$target
;
if
(
$data
[
'UserPageNotice'
]
)
{
$this
->
addNoticeToPage
(
$this
->
getTargetPage
(
NS_USER
,
$target
),
$data
[
'UserPageNoticeText'
],
$data
[
'UserPageNoticePosition'
],
$reason
);
}
if
(
$data
[
'TalkPageNotice'
]
)
{
$this
->
addNoticeToPage
(
$this
->
getTargetPage
(
NS_USER_TALK
,
$target
),
$data
[
'TalkPageNoticeText'
],
$data
[
'TalkPageNoticePosition'
],
$reason
);
}
}
}
$blockedUsersCount
=
count
(
$this
->
blockedUsers
);
$this
->
eventLogger
->
logEvent
(
[
'action'
=>
'block'
,
'targetsCount'
=>
count
(
$targets
),
'relevantTargetsCount'
=>
$blockedUsersCount
,
]
);
if
(
$blockedUsersCount
===
0
)
{
return
[
'checkuser-investigateblock-failure'
];
}
return
true
;
}
/**
* @param int $namespace
* @param string $target Must be a valid IP address or a valid user name
* @return string
*/
private
function
getTargetPage
(
int
$namespace
,
string
$target
):
string
{
if
(
IPUtils
::
isValidRange
(
$target
)
)
{
$target
=
IPUtils
::
sanitizeRange
(
$target
);
}
return
$this
->
titleFormatter
->
getPrefixedText
(
new
TitleValue
(
$namespace
,
$target
)
);
}
/**
* Add a notice to a given page. The notice may be prepended or appended,
* or it may replace the page.
*
* @param string $title Page to which to add the notice
* @param string $notice The notice, as wikitext
* @param string $position One of 'prependtext', 'appendtext' or 'text'
* @param string $summary Edit summary
*/
private
function
addNoticeToPage
(
string
$title
,
string
$notice
,
string
$position
,
string
$summary
):
void
{
$apiParams
=
[
'action'
=>
'edit'
,
'title'
=>
$title
,
$position
=>
$notice
,
'summary'
=>
$summary
,
'token'
=>
$this
->
getContext
()->
getCsrfTokenSet
()->
getToken
(),
];
$api
=
new
ApiMain
(
new
DerivativeRequest
(
$this
->
getRequest
(),
$apiParams
,
// was posted
true
),
// enable write
true
);
try
{
$api
->
execute
();
}
catch
(
Exception
$e
)
{
$this
->
noticesFailed
=
true
;
}
}
/**
* @inheritDoc
*/
public
function
onSuccess
()
{
$blockedUsers
=
array_map
(
function
(
$userName
)
{
$user
=
$this
->
userFactory
->
newFromName
(
$userName
,
UserNameUtils
::
RIGOR_NONE
);
return
Linker
::
userLink
(
$user
->
getId
(),
$userName
);
},
$this
->
blockedUsers
);
$language
=
$this
->
getLanguage
();
$blockedMessage
=
$this
->
msg
(
'checkuser-investigateblock-success'
)
->
rawParams
(
$language
->
listToText
(
$blockedUsers
)
)
->
params
(
$language
->
formatNum
(
count
(
$blockedUsers
)
)
)
->
parseAsBlock
();
$out
=
$this
->
getOutput
();
$out
->
setPageTitleMsg
(
$this
->
msg
(
'blockipsuccesssub'
)
);
$out
->
addHtml
(
$blockedMessage
);
if
(
$this
->
noticesFailed
)
{
$failedNoticesMessage
=
$this
->
msg
(
'checkuser-investigateblock-notices-failed'
);
$out
->
addHtml
(
$failedNoticesMessage
);
}
}
/**
* InvestigateBlock writes to the DB when the form is submitted.
*
* @return true
*/
public
function
doesWrites
()
{
return
true
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Fri, Jul 3, 20:58 (15 h, 23 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
58/78/17ca3178e5fca87b1b896c24f7a3
Default Alt Text
SpecialInvestigateBlock.php (13 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment