27 use InvalidArgumentException;
35 use Psr\Log\LoggerInterface;
66 private $actorStoreFactory;
69 private $blockRestrictionStore;
72 private $commentStore;
78 private $loadBalancer;
81 private $readOnlyMode;
100 LoggerInterface $logger,
108 $wikiId = DatabaseBlock::LOCAL
112 $this->wikiId = $wikiId;
114 $this->options = $options;
115 $this->logger = $logger;
116 $this->actorStoreFactory = $actorStoreFactory;
117 $this->blockRestrictionStore = $blockRestrictionStore;
118 $this->commentStore = $commentStore;
119 $this->hookRunner =
new HookRunner( $hookContainer );
120 $this->loadBalancer = $loadBalancer;
121 $this->readOnlyMode = $readOnlyMode;
122 $this->userFactory = $userFactory;
131 if ( $this->readOnlyMode->isReadOnly( $this->wikiId ) ) {
135 $dbw = $this->loadBalancer->getConnectionRef(
DB_PRIMARY, [], $this->wikiId );
136 $store = $this->blockRestrictionStore;
142 static function (
IDatabase $dbw, $fname ) use ( $store, $limit ) {
149 ->caller( $fname )->fetchFieldValues();
151 $ids = array_map(
'intval', $ids );
152 $store->deleteByBlockId( $ids );
154 ->deleteFrom(
'ipblocks' )
155 ->where( [
'ipb_id' => $ids ] )
156 ->caller( $fname )->execute();
169 private function checkDatabaseDomain( $expectedWiki, ?
IDatabase $db =
null ) {
171 $dbDomain = $db->getDomainID();
172 $storeDomain = $this->loadBalancer->resolveDomainID( $expectedWiki );
173 if ( $dbDomain !== $storeDomain ) {
174 throw new InvalidArgumentException(
175 "DB connection domain '$dbDomain' does not match '$storeDomain'"
179 if ( $expectedWiki !== $this->wikiId ) {
180 throw new InvalidArgumentException(
181 "Must provide a database connection for wiki '$expectedWiki'."
204 if ( !$blocker || $blocker->getName() ===
'' ) {
205 throw new InvalidArgumentException(
'Cannot insert a block without a blocker set' );
208 if ( $database !==
null ) {
210 'Old method signature: Passing a $database is no longer supported',
215 $this->checkDatabaseDomain( $block->
getWikiId(), $database );
217 $this->logger->debug(
'Inserting block; timestamp ' . $block->
getTimestamp() );
221 $dbw = $database ?: $this->loadBalancer->getConnectionRef(
DB_PRIMARY, [], $this->wikiId );
222 $row = $this->getArrayForDatabaseBlock( $block, $dbw );
224 $dbw->newInsertQueryBuilder()
225 ->insertInto(
'ipblocks' )
228 ->caller( __METHOD__ )->execute();
229 $affected = $dbw->affectedRows();
232 $block->
setId( $dbw->insertId() );
234 if ( $restrictions ) {
235 $this->blockRestrictionStore->insert( $restrictions );
244 $ids = $dbw->newSelectQueryBuilder()
247 ->where( [
'ipb_address' => $row[
'ipb_address'],
'ipb_user' => $row[
'ipb_user'] ] )
248 ->andWhere( $dbw->buildComparison(
'<', [
'ipb_expiry' => $dbw->timestamp() ] ) )
249 ->caller( __METHOD__ )->fetchFieldValues();
251 $ids = array_map(
'intval', $ids );
252 $dbw->newDeleteQueryBuilder()
253 ->deleteFrom(
'ipblocks' )
254 ->where( [
'ipb_id' => $ids ] )
255 ->caller( __METHOD__ )->execute();
256 $this->blockRestrictionStore->deleteByBlockId( $ids );
257 $dbw->newInsertQueryBuilder()
258 ->insertInto(
'ipblocks' )
261 ->caller( __METHOD__ )->execute();
262 $affected = $dbw->affectedRows();
264 $block->
setId( $dbw->insertId() );
266 if ( $restrictions ) {
267 $this->blockRestrictionStore->insert( $restrictions );
274 $autoBlockIds = $this->doRetroactiveAutoblock( $block );
278 if ( $targetUserIdentity ) {
279 $targetUser = $this->userFactory->newFromUserIdentity( $targetUserIdentity );
282 $targetUser->setToken();
283 $targetUser->saveSettings();
287 return [
'id' => $block->
getId( $this->wikiId ),
'autoIds' => $autoBlockIds ];
302 $this->logger->debug(
'Updating block; timestamp ' . $block->
getTimestamp() );
304 $this->checkDatabaseDomain( $block->
getWikiId() );
306 $blockId = $block->
getId( $this->wikiId );
308 throw new InvalidArgumentException(
309 __METHOD__ .
" requires that a block id be set\n"
313 $dbw = $this->loadBalancer->getConnectionRef(
DB_PRIMARY, [], $this->wikiId );
315 $row = $this->getArrayForDatabaseBlock( $block, $dbw );
316 $dbw->startAtomic( __METHOD__ );
320 $dbw->newUpdateQueryBuilder()
321 ->update(
'ipblocks' )
323 ->where( [
'ipb_id' => $blockId ] )
324 ->caller( __METHOD__ )->execute();
328 if ( $restrictions !==
null ) {
330 if ( $restrictions === [] ) {
331 $result = $this->blockRestrictionStore->deleteByBlockId( $blockId );
333 $result = $this->blockRestrictionStore->update( $restrictions );
339 $dbw->newUpdateQueryBuilder()
340 ->update(
'ipblocks' )
341 ->set( $this->getArrayForAutoblockUpdate( $block ) )
342 ->where( [
'ipb_parent_block_id' => $blockId ] )
343 ->caller( __METHOD__ )->execute();
346 if ( $restrictions !==
null ) {
347 $this->blockRestrictionStore->updateByParentBlockId(
354 $ids = $dbw->newSelectQueryBuilder()
357 ->where( [
'ipb_parent_block_id' => $blockId ] )
358 ->caller( __METHOD__ )->fetchFieldValues();
360 $ids = array_map(
'intval', $ids );
361 $this->blockRestrictionStore->deleteByBlockId( $ids );
362 $dbw->newDeleteQueryBuilder()
363 ->deleteFrom(
'ipblocks' )
364 ->where( [
'ipb_id' => $ids ] )
365 ->caller( __METHOD__ )->execute();
369 $dbw->endAtomic( __METHOD__ );
372 $autoBlockIds = $this->doRetroactiveAutoblock( $block );
373 return [
'id' => $blockId,
'autoIds' => $autoBlockIds ];
386 if ( $this->readOnlyMode->isReadOnly( $this->wikiId ) ) {
390 $this->checkDatabaseDomain( $block->getWikiId() );
392 $blockId = $block->getId( $this->wikiId );
395 throw new InvalidArgumentException(
396 __METHOD__ .
" requires that a block id be set\n"
399 $dbw = $this->loadBalancer->getConnectionRef(
DB_PRIMARY, [], $this->wikiId );
400 $ids = $dbw->newSelectQueryBuilder()
403 ->where( [
'ipb_parent_block_id' => $blockId ] )
404 ->caller( __METHOD__ )->fetchFieldValues();
405 $ids = array_map(
'intval', $ids );
408 $this->blockRestrictionStore->deleteByBlockId( $ids );
409 $dbw->newDeleteQueryBuilder()
410 ->deleteFrom(
'ipblocks' )
411 ->where( [
'ipb_id' => $ids ] )
412 ->caller( __METHOD__ )->execute();
414 return $dbw->affectedRows() > 0;
425 private function getArrayForDatabaseBlock(
426 DatabaseBlock $block,
429 $expiry = $dbw->encodeExpiry( $block->getExpiry() );
431 if ( $block->getTargetUserIdentity() ) {
432 $userId = $block->getTargetUserIdentity()->getId( $this->wikiId );
436 $blocker = $block->getBlocker();
438 throw new \RuntimeException( __METHOD__ .
': this block does not have a blocker' );
442 $blockerActor = $this->actorStoreFactory
443 ->getActorStore( $dbw->getDomainID() )
444 ->acquireActorId( $blocker, $dbw );
447 'ipb_address' => $block->getTargetName(),
448 'ipb_user' => $userId,
449 'ipb_by_actor' => $blockerActor,
450 'ipb_timestamp' => $dbw->timestamp( $block->getTimestamp() ),
452 'ipb_anon_only' => !$block->isHardblock(),
453 'ipb_create_account' => $block->isCreateAccountBlocked(),
454 'ipb_enable_autoblock' => $block->isAutoblocking(),
455 'ipb_expiry' => $expiry,
456 'ipb_range_start' => $block->getRangeStart(),
457 'ipb_range_end' => $block->getRangeEnd(),
458 'ipb_deleted' => intval( $block->getHideName() ),
459 'ipb_block_email' => $block->isEmailBlocked(),
460 'ipb_allow_usertalk' => $block->isUsertalkEditAllowed(),
461 'ipb_parent_block_id' => $block->getParentBlockId(),
462 'ipb_sitewide' => $block->isSitewide(),
464 $commentArray = $this->commentStore->insert(
467 $block->getReasonComment()
470 $combinedArray = $blockArray + $commentArray;
471 return $combinedArray;
480 private function getArrayForAutoblockUpdate( DatabaseBlock $block ): array {
481 $blocker = $block->getBlocker();
483 throw new \RuntimeException( __METHOD__ .
': this block does not have a blocker' );
485 $dbw = $this->loadBalancer->getConnectionRef(
DB_PRIMARY, [], $this->wikiId );
486 $blockerActor = $this->actorStoreFactory
487 ->getActorNormalization( $this->wikiId )
488 ->acquireActorId( $blocker, $dbw );
490 'ipb_by_actor' => $blockerActor,
491 'ipb_create_account' => $block->isCreateAccountBlocked(),
492 'ipb_deleted' => (int)$block->getHideName(),
493 'ipb_allow_usertalk' => $block->isUsertalkEditAllowed(),
494 'ipb_sitewide' => $block->isSitewide(),
497 $commentArray = $this->commentStore->insert(
500 $block->getReasonComment()
503 $combinedArray = $blockArray + $commentArray;
504 return $combinedArray;
514 private function doRetroactiveAutoblock( DatabaseBlock $block ): array {
517 if ( $block->isAutoblocking() && $block->getType() == AbstractBlock::TYPE_USER ) {
518 $this->logger->debug(
519 'Doing retroactive autoblocks for ' . $block->getTargetName()
522 $hookAutoBlocked = [];
523 $continue = $this->hookRunner->onPerformRetroactiveAutoblock(
529 $coreAutoBlocked = $this->performRetroactiveAutoblock( $block );
530 $autoBlockIds = array_merge( $hookAutoBlocked, $coreAutoBlocked );
532 $autoBlockIds = $hookAutoBlocked;
535 return $autoBlockIds;
545 private function performRetroactiveAutoblock( DatabaseBlock $block ): array {
546 if ( !$this->options->get( MainConfigNames::PutIPinRC ) ) {
551 $type = $block->getType();
552 if ( $type !== AbstractBlock::TYPE_USER ) {
557 $dbr = $this->loadBalancer->getConnectionRef(
DB_REPLICA, [], $this->wikiId );
559 $targetUser = $block->getTargetUserIdentity();
560 $actor = $targetUser ? $this->actorStoreFactory
561 ->getActorNormalization( $this->wikiId )
562 ->findActorId( $targetUser, $dbr ) :
null;
565 $this->logger->debug(
'No actor found to retroactively autoblock' );
569 $rcIp = $dbr->newSelectQueryBuilder()
571 ->from(
'recentchanges' )
572 ->where( [
'rc_actor' => $actor ] )
573 ->orderBy(
'rc_timestamp', SelectQueryBuilder::SORT_DESC )
574 ->caller( __METHOD__ )->fetchField();
577 $this->logger->debug(
'No IP found to retroactively autoblock' );
581 $id = $block->doAutoblock( $rcIp );
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
if(!defined('MW_SETUP_CALLBACK'))
Deferrable Update for closure/callback updates that should use auto-commit mode.
Defer callable updates to run later in the PHP process.
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the pending update queue for execution at the appropriate time.
A class containing constants representing the names of configuration variables.
const UpdateRowsPerQuery
Name constant for the UpdateRowsPerQuery setting, for use with Config::get()
const BlockDisablesLogin
Name constant for the BlockDisablesLogin setting, for use with Config::get()
const PutIPinRC
Name constant for the PutIPinRC setting, for use with Config::get()