Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
43.69% |
90 / 206 |
|
23.81% |
10 / 42 |
CRAP | |
0.00% |
0 / 1 |
DatabaseBlock | |
43.69% |
90 / 206 |
|
23.81% |
10 / 42 |
1603.29 | |
0.00% |
0 / 1 |
__construct | |
93.33% |
28 / 30 |
|
0.00% |
0 / 1 |
4.00 | |||
newFromID | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getQueryInfo | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
equals | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
13 | |||
getRangeCond | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
newFromRow | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
delete | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
insert | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
update | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
isExemptedFromAutoblocks | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
doAutoblock | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
isExpired | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
updateTimestamp | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
getRangeStart | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
30 | |||
getRangeEnd | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
30 | |||
getId | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
setId | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
getParentBlockId | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
isHardblock | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
isAutoblocking | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getRedactedName | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
getAutoblockExpiry | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
purgeExpired | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
newFromTarget | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
newListFromTarget | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getBlocksForIPList | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getType | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getIdentifier | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getRestrictions | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
3.07 | |||
getRawRestrictions | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setRestrictions | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
appliesToTitle | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
appliesToNamespace | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
3.04 | |||
appliesToPage | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
3.04 | |||
appliesToRight | |
30.77% |
4 / 13 |
|
0.00% |
0 / 1 |
13.30 | |||
findRestriction | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
20 | |||
getBlockRestrictionStore | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getBy | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
getByName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
getBlocker | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setBlocker | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
getDBConnection | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Class for blocks stored in the database. |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | */ |
22 | |
23 | namespace MediaWiki\Block; |
24 | |
25 | use InvalidArgumentException; |
26 | use MediaWiki\Block\Restriction\ActionRestriction; |
27 | use MediaWiki\Block\Restriction\NamespaceRestriction; |
28 | use MediaWiki\Block\Restriction\PageRestriction; |
29 | use MediaWiki\Block\Restriction\Restriction; |
30 | use MediaWiki\Html\Html; |
31 | use MediaWiki\MainConfigNames; |
32 | use MediaWiki\MediaWikiServices; |
33 | use MediaWiki\Title\Title; |
34 | use MediaWiki\User\UserIdentity; |
35 | use stdClass; |
36 | use UnexpectedValueException; |
37 | use Wikimedia\IPUtils; |
38 | use Wikimedia\Rdbms\IDatabase; |
39 | |
40 | /** |
41 | * A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give |
42 | * rise to autoblocks and may be tracked with cookies. Such blocks* are more |
43 | * customizable than system blocks: they may be hard blocks, and they may be |
44 | * sitewide or partial. |
45 | * |
46 | * @since 1.34 Renamed from Block. |
47 | */ |
48 | class DatabaseBlock extends AbstractBlock { |
49 | /** @var bool */ |
50 | private $auto; |
51 | |
52 | /** @var int|null */ |
53 | private $parentBlockId; |
54 | |
55 | /** @var int|null */ |
56 | private $id; |
57 | |
58 | /** @var bool */ |
59 | private $isAutoblocking; |
60 | |
61 | /** @var Restriction[] */ |
62 | private $restrictions; |
63 | |
64 | /** @var UserIdentity|null */ |
65 | private $blocker; |
66 | |
67 | /** |
68 | * Create a new block with specified option parameters on a user, IP or IP range. |
69 | * |
70 | * @param array $options Parameters of the block, with options supported by |
71 | * `AbstractBlock::__construct`, and also: |
72 | * - auto: (bool) Is this an automatic block? |
73 | * - expiry: (string) Database timestamp of expiration of the block or 'infinity' |
74 | * - decodedExpiry: (string) The decoded expiry in MW 14-char format or 'infinity' |
75 | * - anonOnly: (bool) Only disallow anonymous actions |
76 | * - createAccount: (bool) Disallow creation of new accounts |
77 | * - enableAutoblock: (bool) Enable automatic blocking |
78 | * - blockEmail: (bool) Disallow sending emails |
79 | * - allowUsertalk: (bool) Allow the target to edit its own talk page |
80 | * - sitewide: (bool) Disallow editing all pages and all contribution actions, |
81 | * except those specifically allowed by other block flags |
82 | * - by: (UserIdentity) UserIdentity object of the blocker. |
83 | * - restrictions: (Restriction[]) Array of partial block restrictions |
84 | * |
85 | * @since 1.26 $options array |
86 | */ |
87 | public function __construct( array $options = [] ) { |
88 | parent::__construct( $options ); |
89 | |
90 | $defaults = [ |
91 | 'id' => null, |
92 | 'parentBlockId' => null, |
93 | 'auto' => false, |
94 | 'expiry' => '', |
95 | 'createAccount' => false, |
96 | 'enableAutoblock' => false, |
97 | 'blockEmail' => false, |
98 | 'allowUsertalk' => false, |
99 | 'sitewide' => true, |
100 | 'by' => null, |
101 | 'restrictions' => null, |
102 | ]; |
103 | |
104 | $options += $defaults; |
105 | |
106 | $this->id = $options['id']; |
107 | $this->parentBlockId = $options['parentBlockId']; |
108 | |
109 | if ( $options['by'] instanceof UserIdentity ) { |
110 | $this->setBlocker( $options['by'] ); |
111 | } |
112 | |
113 | if ( isset( $options['decodedExpiry'] ) ) { |
114 | $this->setExpiry( $options['decodedExpiry'] ); |
115 | } else { |
116 | $this->setExpiry( $this->getDBConnection( DB_REPLICA )->decodeExpiry( $options['expiry'] ) ); |
117 | } |
118 | |
119 | if ( $options['restrictions'] !== null ) { |
120 | $this->setRestrictions( $options['restrictions'] ); |
121 | } |
122 | |
123 | // Boolean settings |
124 | $this->auto = (bool)$options['auto']; |
125 | $this->isAutoblocking( (bool)$options['enableAutoblock'] ); |
126 | $this->isSitewide( (bool)$options['sitewide'] ); |
127 | $this->isEmailBlocked( (bool)$options['blockEmail'] ); |
128 | $this->isCreateAccountBlocked( (bool)$options['createAccount'] ); |
129 | $this->isUsertalkEditAllowed( (bool)$options['allowUsertalk'] ); |
130 | } |
131 | |
132 | /** |
133 | * Load a block from the block ID. |
134 | * |
135 | * @deprecated since 1.42 use DatabaseBlockStore::newFromID() |
136 | * @param int $id ID to search for |
137 | * @return DatabaseBlock|null |
138 | */ |
139 | public static function newFromID( $id ) { |
140 | wfDeprecated( __METHOD__, '1.42' ); |
141 | return MediaWikiServices::getInstance()->getDatabaseBlockStore() |
142 | ->newFromID( $id ); |
143 | } |
144 | |
145 | /** |
146 | * Return the tables, fields, and join conditions to be selected to create |
147 | * a new block object. |
148 | * |
149 | * Since 1.34, ipb_by and ipb_by_text have not been present in the |
150 | * database, but they continue to be available in query results as |
151 | * aliases. |
152 | * |
153 | * @since 1.31 |
154 | * @return array[] With three keys: |
155 | * - tables: (string[]) to include in the `$table` to `IDatabase->select()` |
156 | * or `SelectQueryBuilder::tables` |
157 | * - fields: (string[]) to include in the `$vars` to `IDatabase->select()` |
158 | * or `SelectQueryBuilder::fields` |
159 | * - joins: (array) to include in the `$join_conds` to `IDatabase->select()` |
160 | * or `SelectQueryBuilder::joinConds` |
161 | * @phan-return array{tables:string[],fields:string[],joins:array} |
162 | */ |
163 | public static function getQueryInfo() { |
164 | return MediaWikiServices::getInstance()->getDatabaseBlockStore() |
165 | ->getQueryInfo( DatabaseBlockStore::SCHEMA_IPBLOCKS ); |
166 | } |
167 | |
168 | /** |
169 | * Check if two blocks are effectively equal. Doesn't check irrelevant things like |
170 | * the blocking user or the block timestamp, only things which affect the blocked user |
171 | * |
172 | * @param DatabaseBlock $block |
173 | * @return bool |
174 | */ |
175 | public function equals( DatabaseBlock $block ) { |
176 | return ( |
177 | (string)$this->target == (string)$block->target |
178 | && $this->type == $block->type |
179 | && $this->auto == $block->auto |
180 | && $this->isHardblock() == $block->isHardblock() |
181 | && $this->isCreateAccountBlocked() == $block->isCreateAccountBlocked() |
182 | && $this->getExpiry() == $block->getExpiry() |
183 | && $this->isAutoblocking() == $block->isAutoblocking() |
184 | && $this->getHideName() == $block->getHideName() |
185 | && $this->isEmailBlocked() == $block->isEmailBlocked() |
186 | && $this->isUsertalkEditAllowed() == $block->isUsertalkEditAllowed() |
187 | && $this->getReasonComment()->text == $block->getReasonComment()->text |
188 | && $this->isSitewide() == $block->isSitewide() |
189 | // DatabaseBlock::getRestrictions() may perform a database query, so |
190 | // keep it at the end. |
191 | && $this->getBlockRestrictionStore()->equals( |
192 | $this->getRestrictions(), $block->getRestrictions() |
193 | ) |
194 | ); |
195 | } |
196 | |
197 | /** |
198 | * Get a set of SQL conditions which will select range blocks encompassing a given range |
199 | * @param string $start Hexadecimal IP representation |
200 | * @param string|null $end Hexadecimal IP representation, or null to use $start = $end |
201 | * @return string |
202 | */ |
203 | public static function getRangeCond( $start, $end = null ) { |
204 | return MediaWikiServices::getInstance()->getDatabaseBlockStore() |
205 | ->getRangeCond( $start, $end, DatabaseBlockStore::SCHEMA_IPBLOCKS ); |
206 | } |
207 | |
208 | /** |
209 | * Create a new DatabaseBlock object from a database row |
210 | * @param stdClass $row Row from the ipblocks table |
211 | * @return DatabaseBlock |
212 | */ |
213 | public static function newFromRow( $row ) { |
214 | $services = MediaWikiServices::getInstance(); |
215 | $db = $services->getConnectionProvider()->getReplicaDatabase(); |
216 | return $services->getDatabaseBlockStore()->newFromRow( $db, $row ); |
217 | } |
218 | |
219 | /** |
220 | * Delete the row from the IP blocks table. |
221 | * |
222 | * @deprecated since 1.36 Use DatabaseBlockStore::deleteBlock instead. |
223 | * @return bool |
224 | */ |
225 | public function delete() { |
226 | return MediaWikiServices::getInstance() |
227 | ->getDatabaseBlockStoreFactory() |
228 | ->getDatabaseBlockStore( $this->getWikiId() ) |
229 | ->deleteBlock( $this ); |
230 | } |
231 | |
232 | /** |
233 | * Insert a block into the block table. Will fail if there is a conflicting |
234 | * block (same name and options) already in the database. |
235 | * |
236 | * @deprecated since 1.36 Use DatabaseBlockStore::insertBlock instead. |
237 | * Passing a custom db connection is no longer supported since 1.42. |
238 | * |
239 | * @return bool|array False on failure, assoc array on success: |
240 | * ('id' => block ID, 'autoIds' => array of autoblock IDs) |
241 | */ |
242 | public function insert() { |
243 | return MediaWikiServices::getInstance() |
244 | ->getDatabaseBlockStoreFactory() |
245 | ->getDatabaseBlockStore( $this->getWikiId() ) |
246 | ->insertBlock( $this ); |
247 | } |
248 | |
249 | /** |
250 | * Update a block in the DB with new parameters. |
251 | * The ID field needs to be loaded first. |
252 | * |
253 | * @deprecated since 1.36 Use DatabaseBlockStore::updateBlock instead. |
254 | * @return bool|array False on failure, array on success: |
255 | * ('id' => block ID, 'autoIds' => array of autoblock IDs) |
256 | */ |
257 | public function update() { |
258 | return MediaWikiServices::getInstance() |
259 | ->getDatabaseBlockStoreFactory() |
260 | ->getDatabaseBlockStore( $this->getWikiId() ) |
261 | ->updateBlock( $this ); |
262 | } |
263 | |
264 | /** |
265 | * Checks whether a given IP is on the autoblock exemption list. |
266 | * |
267 | * @since 1.36 |
268 | * |
269 | * @param string $ip The IP to check |
270 | * @return bool |
271 | */ |
272 | public static function isExemptedFromAutoblocks( $ip ) { |
273 | return MediaWikiServices::getInstance()->getAutoblockExemptionList() |
274 | ->isExempt( $ip ); |
275 | } |
276 | |
277 | /** |
278 | * Autoblocks the given IP, referring to this block. |
279 | * |
280 | * @deprecated since 1.42, use DatabaseBlockStore::doAutoblock instead |
281 | * |
282 | * @param string $autoblockIP The IP to autoblock. |
283 | * @return int|false ID if an autoblock was inserted, false if not. |
284 | */ |
285 | public function doAutoblock( $autoblockIP ) { |
286 | return MediaWikiServices::getInstance() |
287 | ->getDatabaseBlockStoreFactory() |
288 | ->getDatabaseBlockStore( $this->getWikiId() ) |
289 | ->doAutoblock( $this, $autoblockIP ); |
290 | } |
291 | |
292 | /** |
293 | * Has the block expired? |
294 | * @return bool |
295 | */ |
296 | public function isExpired() { |
297 | $timestamp = wfTimestampNow(); |
298 | wfDebug( __METHOD__ . " checking current " . $timestamp . " vs $this->expiry" ); |
299 | |
300 | return $this->getExpiry() && $timestamp > $this->getExpiry(); |
301 | } |
302 | |
303 | /** |
304 | * Update the timestamp on autoblocks. |
305 | * |
306 | * @deprecated since 1.42, use DatabaseBlockStore::updateTimestamp instead |
307 | */ |
308 | public function updateTimestamp() { |
309 | MediaWikiServices::getInstance() |
310 | ->getDatabaseBlockStoreFactory() |
311 | ->getDatabaseBlockStore( $this->getWikiId() ) |
312 | ->updateTimestamp( $this ); |
313 | } |
314 | |
315 | /** |
316 | * Get the IP address at the start of the range in Hex form |
317 | * @return string IP in Hex form |
318 | */ |
319 | public function getRangeStart() { |
320 | switch ( $this->type ) { |
321 | case self::TYPE_USER: |
322 | return ''; |
323 | case self::TYPE_IP: |
324 | return IPUtils::toHex( $this->target ); |
325 | case self::TYPE_RANGE: |
326 | [ $start, /*...*/ ] = IPUtils::parseRange( $this->target ); |
327 | return $start; |
328 | default: |
329 | throw new UnexpectedValueException( "Block with invalid type" ); |
330 | } |
331 | } |
332 | |
333 | /** |
334 | * Get the IP address at the end of the range in Hex form |
335 | * @return string IP in Hex form |
336 | */ |
337 | public function getRangeEnd() { |
338 | switch ( $this->type ) { |
339 | case self::TYPE_USER: |
340 | return ''; |
341 | case self::TYPE_IP: |
342 | return IPUtils::toHex( $this->target ); |
343 | case self::TYPE_RANGE: |
344 | [ /*...*/, $end ] = IPUtils::parseRange( $this->target ); |
345 | return $end; |
346 | default: |
347 | throw new UnexpectedValueException( "Block with invalid type" ); |
348 | } |
349 | } |
350 | |
351 | /** |
352 | * @inheritDoc |
353 | */ |
354 | public function getId( $wikiId = self::LOCAL ): ?int { |
355 | $this->assertWiki( $wikiId ); |
356 | return $this->id; |
357 | } |
358 | |
359 | /** |
360 | * Set the block ID |
361 | * |
362 | * @internal Only for use in DatabaseBlockStore; private until 1.36 |
363 | * @param int $blockId |
364 | * @return self |
365 | */ |
366 | public function setId( $blockId ) { |
367 | $this->id = (int)$blockId; |
368 | |
369 | if ( is_array( $this->restrictions ) ) { |
370 | $this->restrictions = $this->getBlockRestrictionStore()->setBlockId( |
371 | $blockId, $this->restrictions |
372 | ); |
373 | } |
374 | |
375 | return $this; |
376 | } |
377 | |
378 | /** |
379 | * @since 1.34 |
380 | * @return int|null If this is an autoblock, ID of the parent block; otherwise null |
381 | */ |
382 | public function getParentBlockId() { |
383 | // Sanity: this shouldn't have been 0, because when it was set in |
384 | // initFromRow() we converted 0 to null, in case the object was serialized |
385 | // and then unserialized, force 0 back to null, see T282890 |
386 | return $this->parentBlockId ?: null; |
387 | } |
388 | |
389 | /** |
390 | * Get/set whether the block is a hard block (affects logged-in users on a given IP/range) |
391 | * @param bool|null $x |
392 | * @return bool |
393 | */ |
394 | public function isHardblock( $x = null ): bool { |
395 | wfSetVar( $this->isHardblock, $x ); |
396 | |
397 | // All user blocks are hard blocks |
398 | return $this->getType() == self::TYPE_USER |
399 | ? true |
400 | : $this->isHardblock; |
401 | } |
402 | |
403 | /** |
404 | * Does the block cause autoblocks to be created? |
405 | * |
406 | * @param null|bool $x |
407 | * @return bool |
408 | */ |
409 | public function isAutoblocking( $x = null ) { |
410 | wfSetVar( $this->isAutoblocking, $x ); |
411 | |
412 | // You can't put an autoblock on an IP or range as we don't have any history to |
413 | // look over to get more IPs from |
414 | return $this->getType() == self::TYPE_USER |
415 | ? $this->isAutoblocking |
416 | : false; |
417 | } |
418 | |
419 | /** |
420 | * Get the block name, but with autoblocked IPs hidden as per standard privacy policy |
421 | * @return string Text is escaped |
422 | */ |
423 | public function getRedactedName() { |
424 | if ( $this->auto ) { |
425 | return Html::element( |
426 | 'span', |
427 | [ 'class' => 'mw-autoblockid' ], |
428 | wfMessage( 'autoblockid', $this->id )->text() |
429 | ); |
430 | } else { |
431 | return htmlspecialchars( $this->getTargetName() ); |
432 | } |
433 | } |
434 | |
435 | /** |
436 | * Get the expiry timestamp for an autoblock created at the given time. |
437 | * |
438 | * @deprecated since 1.42 No replacement, no known callers. |
439 | * |
440 | * @param string|int $timestamp |
441 | * @return string |
442 | */ |
443 | public static function getAutoblockExpiry( $timestamp ) { |
444 | wfDeprecated( __METHOD__, '1.42' ); |
445 | return MediaWikiServices::getInstance()->getDatabaseBlockStore() |
446 | ->getAutoblockExpiry( $timestamp ); |
447 | } |
448 | |
449 | /** |
450 | * Purge expired blocks from the ipblocks table |
451 | * |
452 | * @deprecated since 1.36, hard deprecated since 1.38 |
453 | * Use DatabaseBlockStore::purgeExpiredBlocks instead. |
454 | */ |
455 | public static function purgeExpired() { |
456 | wfDeprecated( __METHOD__, '1.36' ); |
457 | MediaWikiServices::getInstance()->getDatabaseBlockStore()->purgeExpiredBlocks(); |
458 | } |
459 | |
460 | /** |
461 | * Given a target and the target's type, get an existing block object if possible. |
462 | * @param string|UserIdentity|int|null $specificTarget A block target, which may be one of |
463 | * several types: |
464 | * * A user to block, in which case $target will be a User |
465 | * * An IP to block, in which case $target will be a User generated by using |
466 | * User::newFromName( $ip, false ) to turn off name validation |
467 | * * An IP range, in which case $target will be a String "123.123.123.123/18" etc |
468 | * * The ID of an existing block, in the format "#12345" (since pure numbers are valid |
469 | * usernames |
470 | * Calling this with a user, IP address or range will not select autoblocks, and will |
471 | * only select a block where the targets match exactly (so looking for blocks on |
472 | * 1.2.3.4 will not select 1.2.0.0/16 or even 1.2.3.4/32) |
473 | * @param string|UserIdentity|int|null $vagueTarget As above, but we will search for *any* |
474 | * block which affects that target (so for an IP address, get ranges containing that IP; |
475 | * and also get any relevant autoblocks). Leave empty or blank to skip IP-based lookups. |
476 | * @param bool $fromPrimary Whether to use the DB_PRIMARY database |
477 | * @return DatabaseBlock|null (null if no relevant block could be found). The target and type |
478 | * of the returned block will refer to the actual block which was found, which might |
479 | * not be the same as the target you gave if you used $vagueTarget! |
480 | */ |
481 | public static function newFromTarget( |
482 | $specificTarget, |
483 | $vagueTarget = null, |
484 | $fromPrimary = false |
485 | ) { |
486 | return MediaWikiServices::getInstance()->getDatabaseBlockStore() |
487 | ->newFromTarget( $specificTarget, $vagueTarget, $fromPrimary ); |
488 | } |
489 | |
490 | /** |
491 | * This is similar to DatabaseBlock::newFromTarget, but it returns all the relevant blocks. |
492 | * |
493 | * @since 1.34 |
494 | * @param string|UserIdentity|int|null $specificTarget |
495 | * @param string|UserIdentity|int|null $vagueTarget |
496 | * @param bool $fromPrimary |
497 | * @return DatabaseBlock[] Any relevant blocks |
498 | */ |
499 | public static function newListFromTarget( |
500 | $specificTarget, |
501 | $vagueTarget = null, |
502 | $fromPrimary = false |
503 | ) { |
504 | return MediaWikiServices::getInstance()->getDatabaseBlockStore() |
505 | ->newListFromTarget( $specificTarget, $vagueTarget, $fromPrimary ); |
506 | } |
507 | |
508 | /** |
509 | * Get all blocks that match any IP from an array of IP addresses |
510 | * |
511 | * @param array $ipChain List of IPs (strings), usually retrieved from the |
512 | * X-Forwarded-For header of the request |
513 | * @param bool $applySoftBlocks Include soft blocks (anonymous-only blocks). These |
514 | * should only block anonymous and temporary users. |
515 | * @param bool $fromPrimary Whether to query the primary or replica DB |
516 | * @return self[] |
517 | * @since 1.22 |
518 | */ |
519 | public static function getBlocksForIPList( array $ipChain, $applySoftBlocks, $fromPrimary = false ) { |
520 | return MediaWikiServices::getInstance()->getBlockManager() |
521 | ->getBlocksForIPList( $ipChain, $applySoftBlocks, $fromPrimary ); |
522 | } |
523 | |
524 | /** |
525 | * @inheritDoc |
526 | * |
527 | * Autoblocks have whichever type corresponds to their target, so to detect if a block is an |
528 | * autoblock, we have to check the mAuto property instead. |
529 | */ |
530 | public function getType(): ?int { |
531 | return $this->auto |
532 | ? self::TYPE_AUTO |
533 | : parent::getType(); |
534 | } |
535 | |
536 | /** |
537 | * @inheritDoc |
538 | */ |
539 | public function getIdentifier( $wikiId = self::LOCAL ) { |
540 | return $this->getId( $wikiId ); |
541 | } |
542 | |
543 | /** |
544 | * Getting the restrictions will perform a database query if the restrictions |
545 | * are not already loaded. |
546 | * |
547 | * @since 1.33 |
548 | * @return Restriction[] |
549 | */ |
550 | public function getRestrictions() { |
551 | if ( $this->restrictions === null ) { |
552 | // If the block ID has not been set, then do not attempt to load the |
553 | // restrictions. |
554 | if ( !$this->id ) { |
555 | return []; |
556 | } |
557 | $this->restrictions = $this->getBlockRestrictionStore()->loadByBlockId( $this->id ); |
558 | } |
559 | |
560 | return $this->restrictions; |
561 | } |
562 | |
563 | /** |
564 | * Get restrictions without loading from database if not yet loaded |
565 | * |
566 | * @internal |
567 | * @return ?Restriction[] |
568 | */ |
569 | public function getRawRestrictions(): ?array { |
570 | return $this->restrictions; |
571 | } |
572 | |
573 | /** |
574 | * @since 1.33 |
575 | * @param Restriction[] $restrictions |
576 | * @return self |
577 | */ |
578 | public function setRestrictions( array $restrictions ) { |
579 | $this->restrictions = $restrictions; |
580 | return $this; |
581 | } |
582 | |
583 | /** |
584 | * @inheritDoc |
585 | */ |
586 | public function appliesToTitle( Title $title ) { |
587 | if ( $this->isSitewide() ) { |
588 | return true; |
589 | } |
590 | |
591 | $restrictions = $this->getRestrictions(); |
592 | foreach ( $restrictions as $restriction ) { |
593 | if ( $restriction->matches( $title ) ) { |
594 | return true; |
595 | } |
596 | } |
597 | |
598 | return false; |
599 | } |
600 | |
601 | /** |
602 | * @inheritDoc |
603 | */ |
604 | public function appliesToNamespace( $ns ) { |
605 | if ( $this->isSitewide() ) { |
606 | return true; |
607 | } |
608 | |
609 | // Blocks do not apply to virtual namespaces. |
610 | if ( $ns < 0 ) { |
611 | return false; |
612 | } |
613 | |
614 | $restriction = $this->findRestriction( NamespaceRestriction::TYPE, $ns ); |
615 | |
616 | return (bool)$restriction; |
617 | } |
618 | |
619 | /** |
620 | * @inheritDoc |
621 | */ |
622 | public function appliesToPage( $pageId ) { |
623 | if ( $this->isSitewide() ) { |
624 | return true; |
625 | } |
626 | |
627 | // If the pageId is not over zero, the block cannot apply to it. |
628 | if ( $pageId <= 0 ) { |
629 | return false; |
630 | } |
631 | |
632 | $restriction = $this->findRestriction( PageRestriction::TYPE, $pageId ); |
633 | |
634 | return (bool)$restriction; |
635 | } |
636 | |
637 | /** |
638 | * @inheritDoc |
639 | */ |
640 | public function appliesToRight( $right ) { |
641 | // Temporarily access service container until the feature flag is removed: T280532 |
642 | $config = MediaWikiServices::getInstance()->getMainConfig(); |
643 | |
644 | $res = parent::appliesToRight( $right ); |
645 | |
646 | if ( !$res && $config->get( MainConfigNames::EnablePartialActionBlocks ) ) { |
647 | $blockActions = MediaWikiServices::getInstance()->getBlockActionInfo() |
648 | ->getAllBlockActions(); |
649 | |
650 | if ( isset( $blockActions[$right] ) ) { |
651 | $restriction = $this->findRestriction( |
652 | ActionRestriction::TYPE, |
653 | $blockActions[$right] |
654 | ); |
655 | |
656 | // $res may be null or false. This should be preserved if there is no restriction. |
657 | if ( $restriction ) { |
658 | $res = true; |
659 | } |
660 | } |
661 | } |
662 | |
663 | return $res; |
664 | } |
665 | |
666 | /** |
667 | * Find Restriction by type and value. |
668 | * |
669 | * @param string $type |
670 | * @param int $value |
671 | * @return Restriction|null |
672 | */ |
673 | private function findRestriction( $type, $value ) { |
674 | $restrictions = $this->getRestrictions(); |
675 | foreach ( $restrictions as $restriction ) { |
676 | if ( $restriction->getType() !== $type ) { |
677 | continue; |
678 | } |
679 | |
680 | if ( $restriction->getValue() === $value ) { |
681 | return $restriction; |
682 | } |
683 | } |
684 | |
685 | return null; |
686 | } |
687 | |
688 | /** |
689 | * Get a BlockRestrictionStore instance |
690 | * |
691 | * @return BlockRestrictionStore |
692 | */ |
693 | private function getBlockRestrictionStore(): BlockRestrictionStore { |
694 | // TODO: get rid of global state here |
695 | return MediaWikiServices::getInstance() |
696 | ->getBlockRestrictionStoreFactory() |
697 | ->getBlockRestrictionStore( $this->getWikiId() ); |
698 | } |
699 | |
700 | /** |
701 | * @inheritDoc |
702 | */ |
703 | public function getBy( $wikiId = self::LOCAL ): int { |
704 | $this->assertWiki( $wikiId ); |
705 | return ( $this->blocker ) ? $this->blocker->getId( $wikiId ) : 0; |
706 | } |
707 | |
708 | /** |
709 | * @inheritDoc |
710 | */ |
711 | public function getByName() { |
712 | return ( $this->blocker ) ? $this->blocker->getName() : ''; |
713 | } |
714 | |
715 | /** |
716 | * Get the user who implemented this block |
717 | * |
718 | * @return UserIdentity|null user object or null. May be a foreign user. |
719 | */ |
720 | public function getBlocker(): ?UserIdentity { |
721 | return $this->blocker; |
722 | } |
723 | |
724 | /** |
725 | * Set the user who implemented (or will implement) this block |
726 | * |
727 | * @param UserIdentity $user |
728 | */ |
729 | public function setBlocker( $user ) { |
730 | if ( !$user->isRegistered() && |
731 | MediaWikiServices::getInstance()->getUserNameUtils()->isUsable( $user->getName() ) |
732 | ) { |
733 | throw new InvalidArgumentException( |
734 | 'Blocker must be a local user or a name that cannot be a local user' |
735 | ); |
736 | } |
737 | $this->assertWiki( $user->getWikiId() ); |
738 | $this->blocker = $user; |
739 | } |
740 | |
741 | /** |
742 | * @param int $i Specific or virtual (DB_PRIMARY/DB_REPLICA) server index |
743 | * @return IDatabase |
744 | */ |
745 | private function getDBConnection( int $i ) { |
746 | return MediaWikiServices::getInstance() |
747 | ->getDBLoadBalancerFactory() |
748 | ->getMainLB( $this->getWikiId() ) |
749 | ->getConnectionRef( $i, [], $this->getWikiId() ); |
750 | } |
751 | } |