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
;
25 use MediaWiki\CommentStore\CommentStoreComment
;
26 use MediaWiki\DAO\WikiAwareEntityTrait
;
27 use MediaWiki\MainConfigNames
;
28 use MediaWiki\MediaWikiServices
;
29 use MediaWiki\Title\Title
;
30 use MediaWiki\User\User
;
31 use MediaWiki\User\UserIdentity
;
36 * @note Extensions should not subclass this, as MediaWiki currently does not
37 * support custom block types.
38 * @since 1.34 Factored out from DatabaseBlock (previously Block).
40 abstract class AbstractBlock
implements Block
{
41 use WikiAwareEntityTrait
;
43 /** @var CommentStoreComment */
47 protected $timestamp = '';
50 protected $expiry = '';
53 protected $blockEmail = false;
56 protected $allowUsertalk = false;
59 protected $blockCreateAccount = false;
62 protected $hideName = false;
65 protected $isHardblock;
67 /** @var UserIdentity|string|null */
71 * @var int|null AbstractBlock::TYPE_ constant. After the block has been loaded
72 * from the database, this can only be USER, IP or RANGE.
77 protected $isSitewide = true;
79 /** @var string|false */
83 * Create a new block with specified parameters on a user, IP or IP range.
85 * @param array $options Parameters of the block, with supported options:
86 * - address: (string|UserIdentity) Target user name, user identity object,
87 * IP address or IP range
88 * - wiki: (string|false) The wiki the block has been issued in,
89 * self::LOCAL for the local wiki (since 1.38)
90 * - reason: (string|Message|CommentStoreComment) Reason for the block
91 * - timestamp: (string) The time at which the block comes into effect
92 * - hideName: (bool) Hide the target user name
93 * - anonOnly: (bool) Used if the target is an IP address. The block only
94 * applies to anon and temporary users using this IP address, and not to
97 public function __construct( array $options = [] ) {
100 'wiki' => self
::LOCAL
,
107 $options +
= $defaults;
109 $this->wikiId
= $options['wiki'];
110 $this->setTarget( $options['address'] );
111 $this->setReason( $options['reason'] );
112 $this->setTimestamp( wfTimestamp( TS_MW
, $options['timestamp'] ) );
113 $this->setHideName( (bool)$options['hideName'] );
114 $this->isHardblock( !$options['anonOnly'] );
118 * Get the user id of the blocking sysop
120 * @param string|false $wikiId (since 1.38)
121 * @return int (0 for foreign users)
123 abstract public function getBy( $wikiId = self
::LOCAL
): int;
126 * Get the username of the blocking sysop
130 abstract public function getByName();
135 public function getId( $wikiId = self
::LOCAL
): ?
int {
136 $this->assertWiki( $wikiId );
141 * Get the reason given for creating the block, as a string.
143 * Deprecated, since this gives the caller no control over the language
144 * or format, and no access to the comment's data.
146 * @deprecated since 1.35. Use getReasonComment instead.
150 public function getReason() {
151 wfDeprecated( __METHOD__
, '1.35' );
152 $language = RequestContext
::getMain()->getLanguage();
153 return $this->reason
->message
->inLanguage( $language )->plain();
157 * Get the reason for creating the block.
160 * @return CommentStoreComment
162 public function getReasonComment(): CommentStoreComment
{
163 return $this->reason
;
167 * Set the reason for creating the block.
170 * @param string|Message|CommentStoreComment $reason
172 public function setReason( $reason ) {
173 $this->reason
= CommentStoreComment
::newUnsavedComment( $reason );
177 * Get whether the block hides the target's username
180 * @return bool The block hides the username
182 public function getHideName() {
183 return $this->hideName
;
187 * Set whether the block hides the target's username
190 * @param bool $hideName The block hides the username
192 public function setHideName( $hideName ) {
193 $this->hideName
= $hideName;
197 * Indicates that the block is a sitewide block. This means the user is
198 * prohibited from editing any page on the site (other than their own talk
202 * @param null|bool $x
205 public function isSitewide( $x = null ): bool {
206 return wfSetVar( $this->isSitewide
, $x );
210 * Get or set the flag indicating whether this block blocks the target from
211 * creating an account. (Note that the flag may be overridden depending on
215 * @param null|bool $x Value to set (if null, just get the property value)
216 * @return bool Value of the property
218 public function isCreateAccountBlocked( $x = null ): bool {
219 return wfSetVar( $this->blockCreateAccount
, $x );
223 * Get or set the flag indicating whether this block blocks the target from
224 * sending emails. (Note that the flag may be overridden depending on
228 * @param null|bool $x Value to set (if null, just get the property value)
229 * @return bool Value of the property
231 public function isEmailBlocked( $x = null ) {
232 return wfSetVar( $this->blockEmail
, $x );
236 * Get or set the flag indicating whether this block blocks the target from
237 * editing their own user talk page. (Note that the flag may be overridden
238 * depending on global configs.)
241 * @param null|bool $x Value to set (if null, just get the property value)
242 * @return bool Value of the property
244 public function isUsertalkEditAllowed( $x = null ) {
245 return wfSetVar( $this->allowUsertalk
, $x );
249 * Get/set whether the block is a hard block (affects logged-in users on a
252 * Note that temporary users are not considered logged-in here - they are
253 * always blocked by IP-address blocks.
255 * Note that user blocks are always hard blocks, since the target is logged
258 * @since 1.36 Moved up from DatabaseBlock
259 * @param bool|null $x
262 public function isHardblock( $x = null ): bool {
263 wfSetVar( $this->isHardblock
, $x );
265 return $this->getType() == self
::TYPE_USER
267 : $this->isHardblock
;
271 * Determine whether the block prevents a given right. A right may be
272 * allowed or disallowed by default, or determined from a property on the
273 * block object. For certain rights, the property may be overridden
274 * according to global configs.
277 * @param string $right
278 * @return bool|null The block applies to the right, or null if
279 * unsure (e.g. unrecognized right or unset property)
281 public function appliesToRight( $right ) {
282 $blockDisablesLogin = MediaWikiServices
::getInstance()->getMainConfig()
283 ->get( MainConfigNames
::BlockDisablesLogin
);
287 case 'createaccount':
288 $res = $this->isCreateAccountBlocked();
291 $res = $this->isEmailBlocked();
294 // Sitewide blocks always block upload. This may be overridden in a subclass.
295 $res = $this->isSitewide();
301 if ( !$res && $blockDisablesLogin ) {
302 // If a block would disable login, then it should
303 // prevent any right that all users cannot do
304 $permissionManager = MediaWikiServices
::getInstance()->getPermissionManager();
306 $res = $permissionManager->userHasRight( $anon, $right ) ?
$res : true;
313 * Get the type of target for this particular block.
314 * @return int|null AbstractBlock::TYPE_ constant, will never be TYPE_ID
316 public function getType(): ?
int {
322 * @return ?UserIdentity
324 public function getTargetUserIdentity(): ?UserIdentity
{
325 return $this->target
instanceof UserIdentity ?
$this->target
: null;
332 public function getTargetName(): string {
333 return $this->target
instanceof UserIdentity
334 ?
$this->target
->getName()
335 : (string)$this->target
;
339 * @param UserIdentity|string $target
344 public function isBlocking( $target ): bool {
345 $targetName = $target instanceof UserIdentity
349 return $targetName === $this->getTargetName();
353 * Get the block expiry time
358 public function getExpiry(): string {
359 return $this->expiry
;
363 * Set the block expiry time
366 * @param string $expiry
368 public function setExpiry( $expiry ) {
369 // Force string so getExpiry() return typehint doesn't break things
370 $this->expiry
= (string)$expiry;
374 * Get the timestamp indicating when the block was created
379 public function getTimestamp(): string {
380 return $this->timestamp
;
384 * Set the timestamp indicating when the block was created
387 * @param string $timestamp
389 public function setTimestamp( $timestamp ) {
390 // Force string so getTimestamp() return typehint doesn't break things
391 $this->timestamp
= (string)$timestamp;
395 * Set the target for this block, and update $this->type accordingly
396 * @param string|UserIdentity|null $target
398 public function setTarget( $target ) {
399 // Small optimization to make this code testable, this is what would happen anyway
400 if ( $target === '' ) {
401 $this->target
= null;
404 [ $parsedTarget, $this->type
] = MediaWikiServices
::getInstance()
406 ->parseBlockTarget( $target );
407 if ( $parsedTarget !== null ) {
408 $this->assertWiki( is_string( $parsedTarget ) ? self
::LOCAL
: $parsedTarget->getWikiId() );
410 $this->target
= $parsedTarget;
416 * @return string|false
418 public function getWikiId() {
419 return $this->wikiId
;
423 * Get the key and parameters for the corresponding error message.
425 * @deprecated since 1.35 Use BlockErrorFormatter::getMessage instead, and
426 * build the array using Message::getKey and Message::getParams. Hard
427 * deprecated since 1.40.
429 * @param IContextSource $context
430 * @return array A message array: either a list of strings, the first of which
431 * is the message key and the remaining ones the parameters, or an array with
432 * a single MessageSpecifier object.
433 * @phan-return non-empty-array
435 public function getPermissionsError( IContextSource
$context ) {
436 wfDeprecated( __METHOD__
, '1.35' );
437 $message = MediaWikiServices
::getInstance()
438 ->getBlockErrorFormatter()->getMessage(
441 $context->getLanguage(),
442 $context->getRequest()->getIP()
444 return array_merge( [ $message->getKey() ], $message->getParams() );
448 * Determine whether the block allows the user to edit their own
449 * user talk page. This is done separately from
450 * AbstractBlock::appliesToRight because there is no right for
451 * editing one's own user talk page and because the user's talk
452 * page needs to be passed into the block object, which is unaware
455 * The ipb_allow_usertalk flag (which corresponds to the property
456 * allowUsertalk) is used on sitewide blocks and partial blocks
457 * that contain a namespace restriction on the user talk namespace,
458 * but do not contain a page restriction on the user's talk page.
459 * For all other (i.e. most) partial blocks, the flag is ignored,
460 * and the user can always edit their user talk page unless there
461 * is a page restriction on their user talk page, in which case
462 * they can never edit it. (Ideally the flag would be stored as
463 * null in these cases, but the database field isn't nullable.)
465 * This method does not validate that the passed in talk page belongs to the
466 * block target since the target (an IP) might not be the same as the user's
467 * talk page (if they are logged in).
470 * @param Title|null $usertalk The user's user talk page. If null,
471 * and if the target is a User, the target's userpage is used
472 * @return bool The user can edit their talk page
474 public function appliesToUsertalk( Title
$usertalk = null ) {
476 if ( $this->target
instanceof UserIdentity
) {
477 $usertalk = Title
::makeTitle(
479 $this->target
->getName()
482 throw new InvalidArgumentException(
483 '$usertalk must be provided if block target is not a user/IP'
488 if ( $usertalk->getNamespace() !== NS_USER_TALK
) {
489 throw new InvalidArgumentException(
490 '$usertalk must be a user talk page'
494 if ( !$this->isSitewide() ) {
495 if ( $this->appliesToPage( $usertalk->getArticleID() ) ) {
498 if ( !$this->appliesToNamespace( NS_USER_TALK
) ) {
503 // This is a type of block which uses the ipb_allow_usertalk
504 // flag. The flag can still be overridden by global configs.
505 if ( !MediaWikiServices
::getInstance()->getMainConfig()
506 ->get( MainConfigNames
::BlockAllowsUTEdit
)
510 return !$this->isUsertalkEditAllowed();
514 * Checks if a block applies to a particular title
516 * This check does not consider whether `$this->isUsertalkEditAllowed`
517 * returns false, as the identity of the user making the hypothetical edit
518 * isn't known here (particularly in the case of IP hard blocks, range
519 * blocks, and auto-blocks).
521 * @param Title $title
524 public function appliesToTitle( Title
$title ) {
525 return $this->isSitewide();
529 * Checks if a block applies to a particular namespace
536 public function appliesToNamespace( $ns ) {
537 return $this->isSitewide();
541 * Checks if a block applies to a particular page
543 * This check does not consider whether `$this->isUsertalkEditAllowed`
544 * returns false, as the identity of the user making the hypothetical edit
545 * isn't known here (particularly in the case of IP hard blocks, range
546 * blocks, and auto-blocks).
553 public function appliesToPage( $pageId ) {
554 return $this->isSitewide();
558 * Check if the block prevents a user from resetting their password
561 * @return bool The block blocks password reset
563 public function appliesToPasswordReset() {
564 return $this->isCreateAccountBlocked();
567 public function toArray(): array {