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