3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
21 namespace MediaWiki\Block
;
24 use InvalidArgumentException
;
26 use MediaWiki\MediaWikiServices
;
32 * @since 1.34 Factored out from DatabaseBlock (previously Block).
34 abstract class AbstractBlock
{
36 * @deprecated since 1.34. Use getReason and setReason instead.
42 * @deprecated since 1.34. Use getTimestamp and setTimestamp instead.
48 * @deprecated since 1.34. Use getExpiry and setExpiry instead.
54 protected $mBlockEmail = false;
57 protected $allowUsertalk = false;
60 protected $blockCreateAccount = false;
63 * @deprecated since 1.34. Use getHideName and setHideName instead.
66 public $mHideName = false;
68 /** @var User|string */
72 * @var int AbstractBlock::TYPE_ constant. After the block has been loaded
73 * from the database, this can only be USER, IP or RANGE.
81 protected $isSitewide = true;
91 * Create a new block with specified parameters on a user, IP or IP range.
93 * @param array $options Parameters of the block, with supported options:
94 * - address: (string|User) Target user name, User object, IP address or IP range
95 * - by: (int) User ID of the blocker
96 * - reason: (string) Reason of the block
97 * - timestamp: (string) The time at which the block comes into effect
98 * - byText: (string) Username of the blocker (for foreign users)
99 * - hideName: (bool) Hide the target user name
101 public function __construct( array $options = [] ) {
111 $options +
= $defaults;
113 $this->setTarget( $options['address'] );
115 if ( $options['by'] ) {
117 $this->setBlocker( User
::newFromId( $options['by'] ) );
120 $this->setBlocker( $options['byText'] );
123 $this->setReason( $options['reason'] );
124 $this->setTimestamp( wfTimestamp( TS_MW
, $options['timestamp'] ) );
125 $this->setHideName( (bool)$options['hideName'] );
129 * Get the user id of the blocking sysop
131 * @return int (0 for foreign users)
133 public function getBy() {
134 return $this->getBlocker()->getId();
138 * Get the username of the blocking sysop
142 public function getByName() {
143 return $this->getBlocker()->getName();
150 public function getId() {
155 * Get the reason given for creating the block
160 public function getReason() {
161 return $this->mReason
;
165 * Set the reason for creating the block
168 * @param string $reason
170 public function setReason( $reason ) {
171 $this->mReason
= $reason;
175 * Get whether the block hides the target's username
178 * @return bool The block hides the username
180 public function getHideName() {
181 return $this->mHideName
;
185 * Set whether ths block hides the target's username
188 * @param bool $hideName The block hides the username
190 public function setHideName( $hideName ) {
191 $this->mHideName
= $hideName;
195 * Indicates that the block is a sitewide block. This means the user is
196 * prohibited from editing any page on the site (other than their own talk
200 * @param null|bool $x
203 public function isSitewide( $x = null ) {
204 return wfSetVar( $this->isSitewide
, $x );
208 * Get or set the flag indicating whether this block blocks the target from
209 * creating an account. (Note that the flag may be overridden depending on
213 * @param null|bool $x Value to set (if null, just get the property value)
214 * @return bool Value of the property
216 public function isCreateAccountBlocked( $x = null ) {
217 return wfSetVar( $this->blockCreateAccount
, $x );
221 * Get or set the flag indicating whether this block blocks the target from
222 * sending emails. (Note that the flag may be overridden depending on
226 * @param null|bool $x Value to set (if null, just get the property value)
227 * @return bool Value of the property
229 public function isEmailBlocked( $x = null ) {
230 return wfSetVar( $this->mBlockEmail
, $x );
234 * Get or set the flag indicating whether this block blocks the target from
235 * editing their own user talk page. (Note that the flag may be overridden
236 * depending on global configs.)
239 * @param null|bool $x Value to set (if null, just get the property value)
240 * @return bool Value of the property
242 public function isUsertalkEditAllowed( $x = null ) {
243 return wfSetVar( $this->allowUsertalk
, $x );
247 * Determine whether the block prevents a given right. A right
248 * may be blacklisted or whitelisted, or determined from a
249 * property on the block object. For certain rights, the property
250 * may be overridden according to global configs.
253 * @param string $right Right to check
254 * @return bool|null null if unrecognized right or unset property
256 public function appliesToRight( $right ) {
257 $config = RequestContext
::getMain()->getConfig();
258 $blockDisablesLogin = $config->get( 'BlockDisablesLogin' );
263 // TODO: fix this case to return proper value
266 case 'createaccount':
267 $res = $this->isCreateAccountBlocked();
270 $res = $this->isEmailBlocked();
273 // Until T6995 is completed
274 $res = $this->isSitewide();
283 if ( !$res && $blockDisablesLogin ) {
284 // If a block would disable login, then it should
285 // prevent any right that all users cannot do
286 $permissionManager = MediaWikiServices
::getInstance()->getPermissionManager();
288 $res = $permissionManager->userHasRight( $anon, $right ) ?
$res : true;
295 * Get/set whether the block prevents a given action
297 * @deprecated since 1.33, use appliesToRight to determine block
298 * behaviour, and specific methods to get/set properties
299 * @param string $action Action to check
300 * @param bool|null $x Value for set, or null to just get value
301 * @return bool|null Null for unrecognized rights.
303 public function prevents( $action, $x = null ) {
304 $config = RequestContext
::getMain()->getConfig();
305 $blockDisablesLogin = $config->get( 'BlockDisablesLogin' );
306 $blockAllowsUTEdit = $config->get( 'BlockAllowsUTEdit' );
311 # For now... <evil laugh>
314 case 'createaccount':
315 $res = wfSetVar( $this->blockCreateAccount
, $x );
318 $res = wfSetVar( $this->mBlockEmail
, $x );
321 // Until T6995 is completed
322 $res = $this->isSitewide();
324 case 'editownusertalk':
325 // NOTE: this check is not reliable on partial blocks
326 // since partially blocked users are always allowed to edit
327 // their own talk page unless a restriction exists on the
328 // page or User_talk: namespace
329 wfSetVar( $this->allowUsertalk
, $x === null ?
null : !$x );
330 $res = !$this->isUsertalkEditAllowed();
332 // edit own user talk can be disabled by config
333 if ( !$blockAllowsUTEdit ) {
344 if ( !$res && $blockDisablesLogin ) {
345 // If a block would disable login, then it should
346 // prevent any action that all users cannot do
347 $permissionManager = MediaWikiServices
::getInstance()->getPermissionManager();
349 $res = $permissionManager->userHasRight( $anon, $action ) ?
$res : true;
356 * From an existing block, get the target and the type of target.
357 * Note that, except for null, it is always safe to treat the target
358 * as a string; for User objects this will return User::__toString()
359 * which in turn gives User::getName().
361 * @param string|int|User|null $target
362 * @return array [ User|String|null, AbstractBlock::TYPE_ constant|null ]
364 public static function parseTarget( $target ) {
365 # We may have been through this before
366 if ( $target instanceof User
) {
367 if ( IP
::isValid( $target->getName() ) ) {
368 return [ $target, self
::TYPE_IP
];
370 return [ $target, self
::TYPE_USER
];
372 } elseif ( $target === null ) {
373 return [ null, null ];
376 $target = trim( $target );
378 if ( IP
::isValid( $target ) ) {
379 # We can still create a User if it's an IP address, but we need to turn
380 # off validation checking (which would exclude IP addresses)
382 User
::newFromName( IP
::sanitizeIP( $target ), false ),
386 } elseif ( IP
::isValidRange( $target ) ) {
387 # Can't create a User from an IP range
388 return [ IP
::sanitizeRange( $target ), self
::TYPE_RANGE
];
391 # Consider the possibility that this is not a username at all
392 # but actually an old subpage (T31797)
393 if ( strpos( $target, '/' ) !== false ) {
394 # An old subpage, drill down to the user behind it
395 $target = explode( '/', $target )[0];
398 $userObj = User
::newFromName( $target );
399 if ( $userObj instanceof User
) {
400 # Note that since numbers are valid usernames, a $target of "12345" will be
401 # considered a User. If you want to pass a block ID, prepend a hash "#12345",
402 # since hash characters are not valid in usernames or titles generally.
403 return [ $userObj, self
::TYPE_USER
];
405 } elseif ( preg_match( '/^#\d+$/', $target ) ) {
406 # Autoblock reference in the form "#12345"
407 return [ substr( $target, 1 ), self
::TYPE_AUTO
];
410 return [ null, null ];
415 * Get the type of target for this particular block.
416 * @return int AbstractBlock::TYPE_ constant, will never be TYPE_ID
418 public function getType() {
423 * Get the target and target type for this particular block. Note that for autoblocks,
424 * this returns the unredacted name; frontend functions need to call $block->getRedactedName()
426 * @return array [ User|String, AbstractBlock::TYPE_ constant ]
427 * @todo FIXME: This should be an integral part of the block member variables
429 public function getTargetAndType() {
430 return [ $this->getTarget(), $this->getType() ];
434 * Get the target for this particular block. Note that for autoblocks,
435 * this returns the unredacted name; frontend functions need to call $block->getRedactedName()
437 * @return User|string
439 public function getTarget() {
440 return $this->target
;
444 * Get the block expiry time
449 public function getExpiry() {
450 return $this->mExpiry
;
454 * Set the block expiry time
457 * @param string $expiry
459 public function setExpiry( $expiry ) {
460 $this->mExpiry
= $expiry;
464 * Get the timestamp indicating when the block was created
469 public function getTimestamp() {
470 return $this->mTimestamp
;
474 * Set the timestamp indicating when the block was created
477 * @param string $timestamp
479 public function setTimestamp( $timestamp ) {
480 $this->mTimestamp
= $timestamp;
484 * Set the target for this block, and update $this->type accordingly
485 * @param mixed $target
487 public function setTarget( $target ) {
488 list( $this->target
, $this->type
) = static::parseTarget( $target );
492 * Get the user who implemented this block
493 * @return User User object. May name a foreign user.
495 public function getBlocker() {
496 return $this->blocker
;
500 * Set the user who implemented (or will implement) this block
501 * @param User|string $user Local User object or username string
503 public function setBlocker( $user ) {
504 if ( is_string( $user ) ) {
505 $user = User
::newFromName( $user, false );
508 if ( $user->isAnon() && User
::isUsableName( $user->getName() ) ) {
509 throw new InvalidArgumentException(
510 'Blocker must be a local user or a name that cannot be a local user'
514 $this->blocker
= $user;
518 * Get the key and parameters for the corresponding error message.
521 * @param IContextSource $context
524 abstract public function getPermissionsError( IContextSource
$context );
527 * Get block information used in different block error messages
530 * @param IContextSource $context
533 public function getBlockErrorParams( IContextSource
$context ) {
534 $lang = $context->getLanguage();
536 $blocker = $this->getBlocker();
537 if ( $blocker instanceof User
) { // local user
538 $blockerUserpage = $blocker->getUserPage();
539 $blockerText = $lang->embedBidi( $blockerUserpage->getText() );
540 $link = "[[{$blockerUserpage->getPrefixedText()}|{$blockerText}]]";
541 } else { // foreign user
545 $reason = $this->getReason();
546 if ( $reason == '' ) {
547 $reason = $context->msg( 'blockednoreason' )->text();
550 /* $ip returns who *is* being blocked, $intended contains who was meant to be blocked.
551 * This could be a username, an IP range, or a single IP. */
552 $intended = (string)$this->getTarget();
557 $context->getRequest()->getIP(),
558 $lang->embedBidi( $this->getByName() ),
559 // TODO: SystemBlock replaces this with the system block type. Clean up
560 // error params so that this is not necessary.
562 $lang->formatExpiry( $this->getExpiry() ),
563 $lang->embedBidi( $intended ),
564 $lang->userTimeAndDate( $this->getTimestamp(), $context->getUser() ),
569 * Determine whether the block allows the user to edit their own
570 * user talk page. This is done separately from
571 * AbstractBlock::appliesToRight because there is no right for
572 * editing one's own user talk page and because the user's talk
573 * page needs to be passed into the block object, which is unaware
576 * The ipb_allow_usertalk flag (which corresponds to the property
577 * allowUsertalk) is used on sitewide blocks and partial blocks
578 * that contain a namespace restriction on the user talk namespace,
579 * but do not contain a page restriction on the user's talk page.
580 * For all other (i.e. most) partial blocks, the flag is ignored,
581 * and the user can always edit their user talk page unless there
582 * is a page restriction on their user talk page, in which case
583 * they can never edit it. (Ideally the flag would be stored as
584 * null in these cases, but the database field isn't nullable.)
586 * This method does not validate that the passed in talk page belongs to the
587 * block target since the target (an IP) might not be the same as the user's
588 * talk page (if they are logged in).
591 * @param Title|null $usertalk The user's user talk page. If null,
592 * and if the target is a User, the target's userpage is used
593 * @return bool The user can edit their talk page
595 public function appliesToUsertalk( Title
$usertalk = null ) {
597 if ( $this->target
instanceof User
) {
598 $usertalk = $this->target
->getTalkPage();
600 throw new InvalidArgumentException(
601 '$usertalk must be provided if block target is not a user/IP'
606 if ( $usertalk->getNamespace() !== NS_USER_TALK
) {
607 throw new InvalidArgumentException(
608 '$usertalk must be a user talk page'
612 if ( !$this->isSitewide() ) {
613 if ( $this->appliesToPage( $usertalk->getArticleID() ) ) {
616 if ( !$this->appliesToNamespace( NS_USER_TALK
) ) {
621 // This is a type of block which uses the ipb_allow_usertalk
622 // flag. The flag can still be overridden by global configs.
623 $config = RequestContext
::getMain()->getConfig();
624 if ( !$config->get( 'BlockAllowsUTEdit' ) ) {
627 return !$this->isUsertalkEditAllowed();
631 * Checks if a block applies to a particular title
633 * This check does not consider whether `$this->isUsertalkEditAllowed`
634 * returns false, as the identity of the user making the hypothetical edit
635 * isn't known here (particularly in the case of IP hardblocks, range
636 * blocks, and auto-blocks).
638 * @param Title $title
641 public function appliesToTitle( Title
$title ) {
642 return $this->isSitewide();
646 * Checks if a block applies to a particular namespace
653 public function appliesToNamespace( $ns ) {
654 return $this->isSitewide();
658 * Checks if a block applies to a particular page
660 * This check does not consider whether `$this->isUsertalkEditAllowed`
661 * returns false, as the identity of the user making the hypothetical edit
662 * isn't known here (particularly in the case of IP hardblocks, range
663 * blocks, and auto-blocks).
670 public function appliesToPage( $pageId ) {
671 return $this->isSitewide();
675 * Check if the block should be tracked with a cookie.
678 * @deprecated since 1.34 Use BlockManager::trackBlockWithCookie instead
679 * of calling this directly.
680 * @param bool $isAnon The user is logged out
681 * @return bool The block should be tracked with a cookie
683 public function shouldTrackWithCookie( $isAnon ) {
684 wfDeprecated( __METHOD__
, '1.34' );
689 * Check if the block prevents a user from resetting their password
692 * @return bool The block blocks password reset
694 public function appliesToPasswordReset() {
695 return $this->isCreateAccountBlocked();