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