65 private $actorStoreFactory;
68 private $blockRestrictionStore;
71 private $commentStore;
77 private $loadBalancer;
80 private $readOnlyMode;
98 LoggerInterface $logger,
109 $this->options = $options;
110 $this->logger = $logger;
111 $this->actorStoreFactory = $actorStoreFactory;
112 $this->blockRestrictionStore = $blockRestrictionStore;
113 $this->commentStore = $commentStore;
114 $this->hookRunner =
new HookRunner( $hookContainer );
115 $this->loadBalancer = $loadBalancer;
116 $this->readOnlyMode = $readOnlyMode;
117 $this->userFactory = $userFactory;
126 if ( $this->readOnlyMode->isReadOnly() ) {
130 $dbw = $this->loadBalancer->getConnectionRef(
DB_PRIMARY );
131 $store = $this->blockRestrictionStore;
137 static function (
IDatabase $dbw, $fname ) use ( $store, $limit ) {
144 [
'LIMIT' => $limit ]
147 $store->deleteByBlockId( $ids );
148 $dbw->
delete(
'ipblocks', [
'ipb_id' => $ids ], $fname );
161 private function checkDatabaseDomain( ?
IDatabase $db, $expectedWiki ) {
164 $storeDomain = $this->loadBalancer->resolveDomainID( $expectedWiki );
165 if ( $dbDomain !== $storeDomain ) {
166 throw new InvalidArgumentException(
167 "DB connection domain '$dbDomain' does not match '$storeDomain'"
171 if ( $expectedWiki !== Block::LOCAL ) {
172 throw new InvalidArgumentException(
173 "Must provide a database connection for wiki '$expectedWiki'."
195 if ( !$blocker || $blocker->getName() ===
'' ) {
196 throw new MWException(
'Cannot insert a block without a blocker set' );
199 $this->checkDatabaseDomain( $database, $block->
getWikiId() );
201 $this->logger->debug(
'Inserting block; timestamp ' . $block->
getTimestamp() );
206 $dbw = $database ?: $this->loadBalancer->getConnectionRef(
DB_PRIMARY );
207 $row = $this->getArrayForDatabaseBlock( $block, $dbw );
209 $dbw->insert(
'ipblocks', $row, __METHOD__, [
'IGNORE' ] );
210 $affected = $dbw->affectedRows();
213 $block->
setId( $dbw->insertId() );
215 if ( $restrictions ) {
216 $this->blockRestrictionStore->insert( $restrictions );
225 $ids = $dbw->selectFieldValues(
229 'ipb_address' => $row[
'ipb_address'],
230 'ipb_user' => $row[
'ipb_user'],
231 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() )
236 $dbw->delete(
'ipblocks', [
'ipb_id' => $ids ], __METHOD__ );
237 $this->blockRestrictionStore->deleteByBlockId( $ids );
238 $dbw->insert(
'ipblocks', $row, __METHOD__, [
'IGNORE' ] );
239 $affected = $dbw->affectedRows();
240 $block->
setId( $dbw->insertId() );
242 if ( $restrictions ) {
243 $this->blockRestrictionStore->insert( $restrictions );
249 $autoBlockIds = $this->doRetroactiveAutoblock( $block );
253 if ( $targetUserIdentity ) {
254 $targetUser = $this->userFactory->newFromUserIdentity( $targetUserIdentity );
256 $targetUser->setToken();
257 $targetUser->saveSettings();
261 return [
'id' => $block->
getId(),
'autoIds' => $autoBlockIds ];
276 $this->logger->debug(
'Updating block; timestamp ' . $block->
getTimestamp() );
281 '$block->getWikiId()',
282 'must belong to the local wiki.'
284 $blockId = $block->
getId();
287 __METHOD__ .
" requires that a block id be set\n"
291 $dbw = $this->loadBalancer->getConnectionRef(
DB_PRIMARY );
292 $row = $this->getArrayForDatabaseBlock( $block, $dbw );
293 $dbw->startAtomic( __METHOD__ );
295 $result = $dbw->update(
298 [
'ipb_id' => $blockId ],
304 if ( $restrictions !==
null ) {
306 if ( empty( $restrictions ) ) {
307 $success = $this->blockRestrictionStore->deleteByBlockId( $blockId );
309 $success = $this->blockRestrictionStore->update( $restrictions );
319 $this->getArrayForAutoblockUpdate( $block ),
320 [
'ipb_parent_block_id' => $blockId ],
325 if ( $restrictions !==
null ) {
326 $this->blockRestrictionStore->updateByParentBlockId(
333 $this->blockRestrictionStore->deleteByParentBlockId( $blockId );
336 [
'ipb_parent_block_id' => $blockId ],
341 $dbw->endAtomic( __METHOD__ );
344 $autoBlockIds = $this->doRetroactiveAutoblock( $block );
345 return [
'id' => $blockId,
'autoIds' => $autoBlockIds ];
359 if ( $this->readOnlyMode->isReadOnly() ) {
363 $blockId = $block->getId();
367 __METHOD__ .
" requires that a block id be set\n"
370 $dbw = $this->loadBalancer->getConnectionRef(
DB_PRIMARY );
372 $this->blockRestrictionStore->deleteByParentBlockId( $blockId );
375 [
'ipb_parent_block_id' => $blockId ],
379 $this->blockRestrictionStore->deleteByBlockId( $blockId );
382 [
'ipb_id' => $blockId ],
386 return $dbw->affectedRows() > 0;
397 private function getArrayForDatabaseBlock(
398 DatabaseBlock $block,
401 $expiry = $dbw->encodeExpiry( $block->getExpiry() );
403 if ( $block->getTargetUserIdentity() ) {
404 $userId = $block->getTargetUserIdentity()->getId( $block->getWikiId() );
408 $blocker = $block->getBlocker();
410 throw new \RuntimeException( __METHOD__ .
': this block does not have a blocker' );
413 $blockerActor = $this->actorStoreFactory
414 ->getActorStore( $dbw->getDomainID() )
415 ->acquireActorId( $blocker, $dbw );
418 'ipb_address' => $block->getTargetName(),
419 'ipb_user' => $userId,
420 'ipb_by_actor' => $blockerActor,
421 'ipb_timestamp' => $dbw->timestamp( $block->getTimestamp() ),
423 'ipb_anon_only' => !$block->isHardblock(),
424 'ipb_create_account' => $block->isCreateAccountBlocked(),
425 'ipb_enable_autoblock' => $block->isAutoblocking(),
426 'ipb_expiry' => $expiry,
427 'ipb_range_start' => $block->getRangeStart(),
428 'ipb_range_end' => $block->getRangeEnd(),
429 'ipb_deleted' => intval( $block->getHideName() ),
430 'ipb_block_email' => $block->isEmailBlocked(),
431 'ipb_allow_usertalk' => $block->isUsertalkEditAllowed(),
432 'ipb_parent_block_id' => $block->getParentBlockId(),
433 'ipb_sitewide' => $block->isSitewide(),
435 $commentArray = $this->commentStore->insert(
438 $block->getReasonComment()
441 $combinedArray = $blockArray + $commentArray;
442 return $combinedArray;
451 private function getArrayForAutoblockUpdate( DatabaseBlock $block ): array {
452 $blocker = $block->getBlocker();
454 throw new \RuntimeException( __METHOD__ .
': this block does not have a blocker' );
456 $dbw = $this->loadBalancer->getConnectionRef(
DB_PRIMARY );
457 $blockerActor = $this->actorStoreFactory
458 ->getActorNormalization()
459 ->acquireActorId( $blocker, $dbw );
461 'ipb_by_actor' => $blockerActor,
462 'ipb_create_account' => $block->isCreateAccountBlocked(),
463 'ipb_deleted' => (int)$block->getHideName(),
464 'ipb_allow_usertalk' => $block->isUsertalkEditAllowed(),
465 'ipb_sitewide' => $block->isSitewide(),
468 $commentArray = $this->commentStore->insert(
471 $block->getReasonComment()
474 $combinedArray = $blockArray + $commentArray;
475 return $combinedArray;
485 private function doRetroactiveAutoblock( DatabaseBlock $block ): array {
488 if ( $block->isAutoblocking() && $block->getType() == AbstractBlock::TYPE_USER ) {
489 $this->logger->debug(
490 'Doing retroactive autoblocks for ' . $block->getTargetName()
493 $hookAutoBlocked = [];
494 $continue = $this->hookRunner->onPerformRetroactiveAutoblock(
500 $coreAutoBlocked = $this->performRetroactiveAutoblock( $block );
501 $autoBlockIds = array_merge( $hookAutoBlocked, $coreAutoBlocked );
503 $autoBlockIds = $hookAutoBlocked;
506 return $autoBlockIds;
516 private function performRetroactiveAutoblock( DatabaseBlock $block ): array {
517 if ( !$this->options->get( MainConfigNames::PutIPinRC ) ) {
522 $type = $block->getType();
523 if (
$type !== AbstractBlock::TYPE_USER ) {
530 $targetUser = $block->getTargetUserIdentity();
531 $actor = $targetUser ? $this->actorStoreFactory
532 ->getActorNormalization( $block->getWikiId() )
533 ->findActorId( $targetUser,
$dbr ) :
null;
536 $this->logger->debug(
'No actor found to retroactively autoblock' );
540 $rcIp =
$dbr->selectField(
543 [
'rc_actor' => $actor ],
545 [
'ORDER BY' =>
'rc_timestamp DESC' ]
549 $this->logger->debug(
'No IP found to retroactively autoblock' );
553 $id = $block->doAutoblock( $rcIp );