diff --git a/src/Objects/BlockService.php b/src/Objects/BlockService.php index d810020..489bafa 100644 --- a/src/Objects/BlockService.php +++ b/src/Objects/BlockService.php @@ -2,6 +2,8 @@ namespace ActivityPub\Objects; +use ActivityPub\Entities\ActivityPubObject; + class BlockService { /** @@ -23,15 +25,42 @@ class BlockService */ public function getBlockedActorIds( $blockingActorId ) { - $q = array( + $blockQuery = array( 'type' => 'Block', 'actor' => array( 'id' => $blockingActorId, ), ); - $blocks = $this->objectsService->query( $q ); + $blocks = $this->objectsService->query( $blockQuery ); + + // TODO this is janky and slow - there's probably a better way + $undoQuery = array( + 'type' => 'Undo', + 'actor' => array( + 'id' => $blockingActorId, + ), + 'object' => array( + 'type' => 'Block', + ), + ); + $undos = $this->objectsService->query( $undoQuery ); + $undoneBlocks = array(); + foreach ( $undos as $undo ) { + if ( $undo->hasField( 'object' ) ) { + $undoObject = $undo['object']; + if ( is_string( $undoObject ) ) { + $undoneBlocks[$undoObject] = 1; + } else if ( $undoObject instanceof ActivityPubObject && $undoObject->hasField( 'id' ) ) { + $undoneBlocks[$undoObject['id']] = 1; + } + } + } + $blockedIds = array(); foreach ( $blocks as $block ) { + if ( array_key_exists( $block['id'], $undoneBlocks ) ) { + continue; + } if ( $block->hasField( 'object' ) ) { $blockedActor = $block['object']; if ( is_string( $blockedActor ) ) { diff --git a/src/Objects/ObjectsService.php b/src/Objects/ObjectsService.php index e8176a7..52ef17d 100644 --- a/src/Objects/ObjectsService.php +++ b/src/Objects/ObjectsService.php @@ -27,6 +27,11 @@ class ObjectsService */ protected $httpClient; + /** + * @var int + */ + protected $nonce; + public function __construct( EntityManager $entityManager, DateTimeProvider $dateTimeProvider, Client $client ) @@ -34,6 +39,7 @@ class ObjectsService $this->entityManager = $entityManager; $this->dateTimeProvider = $dateTimeProvider; $this->httpClient = $client; + $this->nonce = 0; } /** @@ -97,6 +103,12 @@ class ObjectsService return $query->getResult(); } + protected function getNextNonce() + { + $this->nonce = $this->nonce + 1; + return $this->nonce; + } + /** * Generates the Doctrine QueryBuilder that represents the query * @@ -104,16 +116,16 @@ class ObjectsService * final expression * * @param array $queryTerms The query terms from which to generate the expressions - * @param int $nonce A nonce value to differentiate field names * @return QueryBuilder The expression */ - protected function getObjectQuery( $queryTerms, $nonce = 0 ) + protected function getObjectQuery( $queryTerms ) { + $nonce = $this->getNextNonce(); $qb = $this->entityManager->createQueryBuilder(); $exprs = array(); foreach ( $queryTerms as $fieldName => $fieldValue ) { if ( is_array( $fieldValue ) ) { - $subQuery = $this->getObjectQuery( $fieldValue, $nonce + 1 ); + $subQuery = $this->getObjectQuery( $fieldValue ); $exprs[] = $qb->expr()->andX( $qb->expr()->like( "field$nonce.name", diff --git a/test/Objects/BlockServiceTest.php b/test/Objects/BlockServiceTest.php index 2ea59fe..3750935 100644 --- a/test/Objects/BlockServiceTest.php +++ b/test/Objects/BlockServiceTest.php @@ -156,6 +156,94 @@ class BlockServiceTest extends SQLiteTestCase 'blockingActorId' => 'https://example.com/actors/1', 'expectedBlockedActorIds' => array( 'https://elsewhere.com/actors/2' ), ), + array( + 'id' => 'undoneBlocks', + 'initialData' => array( + array( + 'id' => 'https://example.com/blocks/1', + 'type' => 'Block', + 'actor' => array( + 'id' => 'https://example.com/actors/1', + ), + 'object' => array( + 'id' => 'https://elsewhere.com/actors/1', + ) + ), + array( + 'id' => 'https://example.com/blocks/2', + 'type' => 'Block', + 'actor' => array( + 'id' => 'https://example.com/actors/1', + ), + 'object' => array( + 'id' => 'https://elsewhere.com/actors/2', + ) + ), + array( + 'id' => 'https://example.com/undos/1', + 'type' => 'Undo', + 'actor' => array( + 'id' => 'https://example.com/actors/1', + ), + 'object' => array( + 'id' => 'https://example.com/blocks/1', + 'type' => 'Block', + 'actor' => array( + 'id' => 'https://example.com/actors/1', + ), + 'object' => array( + 'id' => 'https://elsewhere.com/actors/1', + ) + ) + ), + ), + 'blockingActorId' => 'https://example.com/actors/1', + 'expectedBlockedActorIds' => array( 'https://elsewhere.com/actors/2' ), + ), + array( + 'id' => 'irrelevantUndonBlocks', + 'initialData' => array( + array( + 'id' => 'https://example.com/blocks/1', + 'type' => 'Block', + 'actor' => array( + 'id' => 'https://example.com/actors/1', + ), + 'object' => array( + 'id' => 'https://elsewhere.com/actors/1', + ) + ), + array( + 'id' => 'https://example.com/blocks/2', + 'type' => 'Block', + 'actor' => array( + 'id' => 'https://example.com/actors/1', + ), + 'object' => array( + 'id' => 'https://elsewhere.com/actors/2', + ) + ), + array( + 'id' => 'https://example.com/undos/1', + 'type' => 'Undo', + 'actor' => array( + 'id' => 'https://example.com/actors/1', + ), + 'object' => array( + 'id' => 'https://example.com/blocks/3', + 'type' => 'Block', + 'actor' => array( + 'id' => 'https://example.com/actors/1', + ), + 'object' => array( + 'id' => 'https://elsewhere.com/actors/3', + ) + ) + ), + ), + 'blockingActorId' => 'https://example.com/actors/1', + 'expectedBlockedActorIds' => array( 'https://elsewhere.com/actors/1', 'https://elsewhere.com/actors/2' ), + ) ); foreach ( $testCases as $testCase ) { self::setUp();