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
;
31 * @since 1.34 Factored out from DatabaseBlock (previously Block).
33 abstract class AbstractBlock
{
35 * @deprecated since 1.34. Use getReason and setReason instead.
41 * @deprecated since 1.34. Use getTimestamp and setTimestamp instead.
47 * @deprecated since 1.34. Use getExpiry and setExpiry instead.
53 protected $mBlockEmail = false;
56 protected $allowUsertalk = false;
59 protected $blockCreateAccount = false;
62 * @deprecated since 1.34. Use getHideName and setHideName instead.
65 public $mHideName = false;
67 /** @var User|string */
71 * @var int AbstractBlock::TYPE_ constant. After the block has been loaded
72 * from the database, this can only be USER, IP or RANGE.
80 protected $isSitewide = true;
90 * Create a new block with specified parameters on a user, IP or IP range.
92 * @param array $options Parameters of the block:
93 * address string|User Target user name, User object, IP address or IP range
94 * by int User ID of the blocker
95 * reason string Reason of the block
96 * timestamp string The time at which the block comes into effect
97 * byText string Username of the blocker (for foreign users)
99 public function __construct( array $options = [] ) {
108 $options +
= $defaults;
110 $this->setTarget( $options['address'] );
112 if ( $options['by'] ) {
114 $this->setBlocker( User
::newFromId( $options['by'] ) );
117 $this->setBlocker( $options['byText'] );
120 $this->setReason( $options['reason'] );
121 $this->setTimestamp( wfTimestamp( TS_MW
, $options['timestamp'] ) );
125 * Get the user id of the blocking sysop
127 * @return int (0 for foreign users)
129 public function getBy() {
130 return $this->getBlocker()->getId();
134 * Get the username of the blocking sysop
138 public function getByName() {
139 return $this->getBlocker()->getName();
146 public function getId() {
151 * Get the reason given for creating the block
156 public function getReason() {
157 return $this->mReason
;
161 * Set the reason for creating the block
164 * @param string $reason
166 public function setReason( $reason ) {
167 $this->mReason
= $reason;
171 * Get whether the block hides the target's username
174 * @return bool The block hides the username
176 public function getHideName() {
177 return $this->mHideName
;
181 * Set whether ths block hides the target's username
184 * @param bool $hideName The block hides the username
186 public function setHideName( $hideName ) {
187 $this->mHideName
= $hideName;
191 * Indicates that the block is a sitewide block. This means the user is
192 * prohibited from editing any page on the site (other than their own talk
196 * @param null|bool $x
199 public function isSitewide( $x = null ) {
200 return wfSetVar( $this->isSitewide
, $x );
204 * Get or set the flag indicating whether this block blocks the target from
205 * creating an account. (Note that the flag may be overridden depending on
209 * @param null|bool $x Value to set (if null, just get the property value)
210 * @return bool Value of the property
212 public function isCreateAccountBlocked( $x = null ) {
213 return wfSetVar( $this->blockCreateAccount
, $x );
217 * Get or set the flag indicating whether this block blocks the target from
218 * sending emails. (Note that the flag may be overridden depending on
222 * @param null|bool $x Value to set (if null, just get the property value)
223 * @return bool Value of the property
225 public function isEmailBlocked( $x = null ) {
226 return wfSetVar( $this->mBlockEmail
, $x );
230 * Get or set the flag indicating whether this block blocks the target from
231 * editing their own user talk page. (Note that the flag may be overridden
232 * depending on global configs.)
235 * @param null|bool $x Value to set (if null, just get the property value)
236 * @return bool Value of the property
238 public function isUsertalkEditAllowed( $x = null ) {
239 return wfSetVar( $this->allowUsertalk
, $x );
243 * Determine whether the block prevents a given right. A right
244 * may be blacklisted or whitelisted, or determined from a
245 * property on the block object. For certain rights, the property
246 * may be overridden according to global configs.
249 * @param string $right Right to check
250 * @return bool|null null if unrecognized right or unset property
252 public function appliesToRight( $right ) {
253 $config = RequestContext
::getMain()->getConfig();
254 $blockDisablesLogin = $config->get( 'BlockDisablesLogin' );
259 // TODO: fix this case to return proper value
262 case 'createaccount':
263 $res = $this->isCreateAccountBlocked();
266 $res = $this->isEmailBlocked();
269 // Until T6995 is completed
270 $res = $this->isSitewide();
279 if ( !$res && $blockDisablesLogin ) {
280 // If a block would disable login, then it should
281 // prevent any right that all users cannot do
283 $res = $anon->isAllowed( $right ) ?
$res : true;
290 * Get/set whether the block prevents a given action
292 * @deprecated since 1.33, use appliesToRight to determine block
293 * behaviour, and specific methods to get/set properties
294 * @param string $action Action to check
295 * @param bool|null $x Value for set, or null to just get value
296 * @return bool|null Null for unrecognized rights.
298 public function prevents( $action, $x = null ) {
299 $config = RequestContext
::getMain()->getConfig();
300 $blockDisablesLogin = $config->get( 'BlockDisablesLogin' );
301 $blockAllowsUTEdit = $config->get( 'BlockAllowsUTEdit' );
306 # For now... <evil laugh>
309 case 'createaccount':
310 $res = wfSetVar( $this->blockCreateAccount
, $x );
313 $res = wfSetVar( $this->mBlockEmail
, $x );
316 // Until T6995 is completed
317 $res = $this->isSitewide();
319 case 'editownusertalk':
320 // NOTE: this check is not reliable on partial blocks
321 // since partially blocked users are always allowed to edit
322 // their own talk page unless a restriction exists on the
323 // page or User_talk: namespace
324 wfSetVar( $this->allowUsertalk
, $x === null ?
null : !$x );
325 $res = !$this->isUsertalkEditAllowed();
327 // edit own user talk can be disabled by config
328 if ( !$blockAllowsUTEdit ) {
339 if ( !$res && $blockDisablesLogin ) {
340 // If a block would disable login, then it should
341 // prevent any action that all users cannot do
343 $res = $anon->isAllowed( $action ) ?
$res : true;
350 * From an existing block, get the target and the type of target.
351 * Note that, except for null, it is always safe to treat the target
352 * as a string; for User objects this will return User::__toString()
353 * which in turn gives User::getName().
355 * @param string|int|User|null $target
356 * @return array [ User|String|null, AbstractBlock::TYPE_ constant|null ]
358 public static function parseTarget( $target ) {
359 # We may have been through this before
360 if ( $target instanceof User
) {
361 if ( IP
::isValid( $target->getName() ) ) {
362 return [ $target, self
::TYPE_IP
];
364 return [ $target, self
::TYPE_USER
];
366 } elseif ( $target === null ) {
367 return [ null, null ];
370 $target = trim( $target );
372 if ( IP
::isValid( $target ) ) {
373 # We can still create a User if it's an IP address, but we need to turn
374 # off validation checking (which would exclude IP addresses)
376 User
::newFromName( IP
::sanitizeIP( $target ), false ),
380 } elseif ( IP
::isValidRange( $target ) ) {
381 # Can't create a User from an IP range
382 return [ IP
::sanitizeRange( $target ), self
::TYPE_RANGE
];
385 # Consider the possibility that this is not a username at all
386 # but actually an old subpage (T31797)
387 if ( strpos( $target, '/' ) !== false ) {
388 # An old subpage, drill down to the user behind it
389 $target = explode( '/', $target )[0];
392 $userObj = User
::newFromName( $target );
393 if ( $userObj instanceof User
) {
394 # Note that since numbers are valid usernames, a $target of "12345" will be
395 # considered a User. If you want to pass a block ID, prepend a hash "#12345",
396 # since hash characters are not valid in usernames or titles generally.
397 return [ $userObj, self
::TYPE_USER
];
399 } elseif ( preg_match( '/^#\d+$/', $target ) ) {
400 # Autoblock reference in the form "#12345"
401 return [ substr( $target, 1 ), self
::TYPE_AUTO
];
404 return [ null, null ];
409 * Get the type of target for this particular block.
410 * @return int AbstractBlock::TYPE_ constant, will never be TYPE_ID
412 public function getType() {
417 * Get the target and target type for this particular block. Note that for autoblocks,
418 * this returns the unredacted name; frontend functions need to call $block->getRedactedName()
420 * @return array [ User|String, AbstractBlock::TYPE_ constant ]
421 * @todo FIXME: This should be an integral part of the block member variables
423 public function getTargetAndType() {
424 return [ $this->getTarget(), $this->getType() ];
428 * Get the target for this particular block. Note that for autoblocks,
429 * this returns the unredacted name; frontend functions need to call $block->getRedactedName()
431 * @return User|string
433 public function getTarget() {
434 return $this->target
;
438 * Get the block expiry time
443 public function getExpiry() {
444 return $this->mExpiry
;
448 * Set the block expiry time
451 * @param string $expiry
453 public function setExpiry( $expiry ) {
454 $this->mExpiry
= $expiry;
458 * Get the timestamp indicating when the block was created
463 public function getTimestamp() {
464 return $this->mTimestamp
;
468 * Set the timestamp indicating when the block was created
471 * @param string $timestamp
473 public function setTimestamp( $timestamp ) {
474 $this->mTimestamp
= $timestamp;
478 * Set the target for this block, and update $this->type accordingly
479 * @param mixed $target
481 public function setTarget( $target ) {
482 list( $this->target
, $this->type
) = static::parseTarget( $target );
486 * Get the user who implemented this block
487 * @return User User object. May name a foreign user.
489 public function getBlocker() {
490 return $this->blocker
;
494 * Set the user who implemented (or will implement) this block
495 * @param User|string $user Local User object or username string
497 public function setBlocker( $user ) {
498 if ( is_string( $user ) ) {
499 $user = User
::newFromName( $user, false );
502 if ( $user->isAnon() && User
::isUsableName( $user->getName() ) ) {
503 throw new InvalidArgumentException(
504 'Blocker must be a local user or a name that cannot be a local user'
508 $this->blocker
= $user;
512 * Get the key and parameters for the corresponding error message.
515 * @param IContextSource $context
518 abstract public function getPermissionsError( IContextSource
$context );
521 * Get block information used in different block error messages
524 * @param IContextSource $context
527 public function getBlockErrorParams( IContextSource
$context ) {
528 $lang = $context->getLanguage();
530 $blocker = $this->getBlocker();
531 if ( $blocker instanceof User
) { // local user
532 $blockerUserpage = $blocker->getUserPage();
533 $blockerText = $lang->embedBidi( $blockerUserpage->getText() );
534 $link = "[[{$blockerUserpage->getPrefixedText()}|{$blockerText}]]";
535 } else { // foreign user
539 $reason = $this->getReason();
540 if ( $reason == '' ) {
541 $reason = $context->msg( 'blockednoreason' )->text();
544 /* $ip returns who *is* being blocked, $intended contains who was meant to be blocked.
545 * This could be a username, an IP range, or a single IP. */
546 $intended = (string)$this->getTarget();
551 $context->getRequest()->getIP(),
552 $lang->embedBidi( $this->getByName() ),
553 // TODO: SystemBlock replaces this with the system block type. Clean up
554 // error params so that this is not necessary.
556 $lang->formatExpiry( $this->getExpiry() ),
557 $lang->embedBidi( $intended ),
558 $lang->userTimeAndDate( $this->getTimestamp(), $context->getUser() ),
563 * Determine whether the block allows the user to edit their own
564 * user talk page. This is done separately from
565 * AbstractBlock::appliesToRight because there is no right for
566 * editing one's own user talk page and because the user's talk
567 * page needs to be passed into the block object, which is unaware
570 * The ipb_allow_usertalk flag (which corresponds to the property
571 * allowUsertalk) is used on sitewide blocks and partial blocks
572 * that contain a namespace restriction on the user talk namespace,
573 * but do not contain a page restriction on the user's talk page.
574 * For all other (i.e. most) partial blocks, the flag is ignored,
575 * and the user can always edit their user talk page unless there
576 * is a page restriction on their user talk page, in which case
577 * they can never edit it. (Ideally the flag would be stored as
578 * null in these cases, but the database field isn't nullable.)
580 * This method does not validate that the passed in talk page belongs to the
581 * block target since the target (an IP) might not be the same as the user's
582 * talk page (if they are logged in).
585 * @param Title|null $usertalk The user's user talk page. If null,
586 * and if the target is a User, the target's userpage is used
587 * @return bool The user can edit their talk page
589 public function appliesToUsertalk( Title
$usertalk = null ) {
591 if ( $this->target
instanceof User
) {
592 $usertalk = $this->target
->getTalkPage();
594 throw new InvalidArgumentException(
595 '$usertalk must be provided if block target is not a user/IP'
600 if ( $usertalk->getNamespace() !== NS_USER_TALK
) {
601 throw new InvalidArgumentException(
602 '$usertalk must be a user talk page'
606 if ( !$this->isSitewide() ) {
607 if ( $this->appliesToPage( $usertalk->getArticleID() ) ) {
610 if ( !$this->appliesToNamespace( NS_USER_TALK
) ) {
615 // This is a type of block which uses the ipb_allow_usertalk
616 // flag. The flag can still be overridden by global configs.
617 $config = RequestContext
::getMain()->getConfig();
618 if ( !$config->get( 'BlockAllowsUTEdit' ) ) {
621 return !$this->isUsertalkEditAllowed();
625 * Checks if a block applies to a particular title
627 * This check does not consider whether `$this->isUsertalkEditAllowed`
628 * returns false, as the identity of the user making the hypothetical edit
629 * isn't known here (particularly in the case of IP hardblocks, range
630 * blocks, and auto-blocks).
632 * @param Title $title
635 public function appliesToTitle( Title
$title ) {
636 return $this->isSitewide();
640 * Checks if a block applies to a particular namespace
647 public function appliesToNamespace( $ns ) {
648 return $this->isSitewide();
652 * Checks if a block applies to a particular page
654 * This check does not consider whether `$this->isUsertalkEditAllowed`
655 * returns false, as the identity of the user making the hypothetical edit
656 * isn't known here (particularly in the case of IP hardblocks, range
657 * blocks, and auto-blocks).
664 public function appliesToPage( $pageId ) {
665 return $this->isSitewide();
669 * Check if the block should be tracked with a cookie.
672 * @deprecated since 1.34 Use BlockManager::trackBlockWithCookie instead
673 * of calling this directly.
674 * @param bool $isAnon The user is logged out
675 * @return bool The block should be tracked with a cookie
677 public function shouldTrackWithCookie( $isAnon ) {
678 wfDeprecated( __METHOD__
, '1.34' );
683 * Check if the block prevents a user from resetting their password
686 * @return bool The block blocks password reset
688 public function appliesToPasswordReset() {
689 return $this->isCreateAccountBlocked();