From 0a3ce77893b6f290d2856188c661e580b84d8a7c Mon Sep 17 00:00:00 2001 From: Jeremy Dormitzer Date: Sun, 24 Mar 2019 11:31:37 -0400 Subject: [PATCH] Implement UndoHandler; register all activity handlers --- src/ActivityEventHandlers/UndoHandler.php | 156 ++++++++++++++++++++++ src/ActivityPub.php | 8 ++ src/Config/ActivityPubModule.php | 22 +++ 3 files changed, 186 insertions(+) create mode 100644 src/ActivityEventHandlers/UndoHandler.php diff --git a/src/ActivityEventHandlers/UndoHandler.php b/src/ActivityEventHandlers/UndoHandler.php new file mode 100644 index 0000000..6d66a69 --- /dev/null +++ b/src/ActivityEventHandlers/UndoHandler.php @@ -0,0 +1,156 @@ + 'handleInbox', + OutboxActivityEvent::NAME => 'handleOutbox', + ); + } + + public function __construct( ObjectsService $objectsService, + CollectionsService $collectionsService ) + { + $this->objectsService = $objectsService; + $this->collectionsService = $collectionsService; + } + + // make sure actors match for undo activity and its object + // Undoing likes: remove from likes/liked collection + // Undoing follow: remove from following/followers collection + + public function handleInbox( InboxActivityEvent $event ) + { + $activity = $event->getActivity(); + if ( $activity['type'] !== 'Undo' ) { + return; + } + $object = $this->getUndoObject( $activity ); + if ( ! ( $object && $object->hasField( 'type' ) ) ) { + return; + } + if ( ! $this->undoIsValid( $activity, $object ) ) { + return; + } + switch ( $object['type'] ) { + case 'Follow': + $this->removeFromCollection( $object['object'], 'followers', $object['actor'] ); + break; + case 'Like': + $this->removeFromCollection( $object['object'], 'likes', $object['actor'] ); + break; + default: + return; + } + } + + public function handleOutbox( OutboxActivityEvent $event ) + { + $activity = $event->getActivity(); + if ( $activity['type'] !== 'Undo' ) { + return; + } + $object = $this->getUndoObject( $activity ); + if ( ! ( $object && $object->hasField( 'type' ) ) ) { + return; + } + if ( ! $this->undoIsValid( $activity, $object ) ) { + return; + } + switch ( $object['type'] ) { + case 'Follow': + $this->removeFromCollection( $object['actor'], 'following', $object['object'] ); + break; + case 'Like': + $this->removeFromCollection( $object['actor'], 'liked', $object['object'] ); + break; + default: + return; + } + } + + private function undoIsValid( $activity, ActivityPubObject $undoObject ) + { + if ( ! array_key_exists( 'actor', $activity ) ) { + return false; + } + $actorId = $activity['actor']; + if ( is_array( $actorId ) && array_key_exists( 'id', $actorId ) ) { + $actorId = $actorId['id']; + } + if ( ! is_string( $actorId ) ) { + return false; + } + $objectActor = $undoObject['actor']; + if ( ! $objectActor ) { + return false; + } + return $actorId == $objectActor['id']; + } + + private function removeFromCollection( $object, $collectionField, $itemId ) + { + if ( ! ( $object && $object instanceof ActivityPubObject ) ) { + return; + } + if ( ! $object->hasField( $collectionField ) ) { + return; + } + $collection = $object[$collectionField]; + if ( ! ( $collection && $collection instanceof ActivityPubObject ) ) { + return; + } + if ( ! $itemId ) { + return; + } + if ( $itemId instanceof ActivityPubObject && $itemId->hasField( 'id' ) ) { + $itemId = $itemId['id']; + } else if ( is_array( $itemId ) && array_key_exists( 'id', $itemId ) ) { + $itemId = $itemId['id']; + } + if ( ! is_string( $itemId ) ) { + return; + } + $this->collectionsService->removeItem( $collection, $itemId ); + } + + /** + * Gets the object of the undo activity as an ActivityPubObject + * @param $activity + * @return \ActivityPub\Entities\ActivityPubObject|null + */ + private function getUndoObject( $activity ) + { + $objectId = $activity['object']; + if ( is_array( $objectId ) ) { + if ( ! array_key_exists( 'id', $objectId ) ) { + return null; + } + $objectId = $objectId['id']; + } + $object = $this->objectsService->dereference( $objectId ); + if ( ! $object ) { + return null; + } + return $object; + } +} \ No newline at end of file diff --git a/src/ActivityPub.php b/src/ActivityPub.php index e853137..874e64d 100644 --- a/src/ActivityPub.php +++ b/src/ActivityPub.php @@ -6,10 +6,14 @@ namespace ActivityPub; use ActivityPub\ActivityEventHandlers\AcceptHandler; use ActivityPub\ActivityEventHandlers\AddHandler; +use ActivityPub\ActivityEventHandlers\AnnounceHandler; use ActivityPub\ActivityEventHandlers\CreateHandler; use ActivityPub\ActivityEventHandlers\DeleteHandler; use ActivityPub\ActivityEventHandlers\FollowHandler; +use ActivityPub\ActivityEventHandlers\LikeHandler; use ActivityPub\ActivityEventHandlers\NonActivityHandler; +use ActivityPub\ActivityEventHandlers\RemoveHandler; +use ActivityPub\ActivityEventHandlers\UndoHandler; use ActivityPub\ActivityEventHandlers\UpdateHandler; use ActivityPub\ActivityEventHandlers\ValidationHandler; use ActivityPub\Auth\AuthListener; @@ -92,6 +96,10 @@ class ActivityPub $dispatcher->addSubscriber( $this->module->get( FollowHandler::class ) ); $dispatcher->addSubscriber( $this->module->get( AcceptHandler::class ) ); $dispatcher->addSubscriber( $this->module->get( AddHandler::class ) ); + $dispatcher->addSubscriber( $this->module->get( RemoveHandler::class ) ); + $dispatcher->addSubscriber( $this->module->get( LikeHandler::class ) ); + $dispatcher->addSubscriber( $this->module->get( AnnounceHandler::class ) ); + $dispatcher->addSubscriber( $this->module->get( UndoHandler::class ) ); } /** diff --git a/src/Config/ActivityPubModule.php b/src/Config/ActivityPubModule.php index 99914a8..8369391 100644 --- a/src/Config/ActivityPubModule.php +++ b/src/Config/ActivityPubModule.php @@ -6,10 +6,14 @@ namespace ActivityPub\Config; use ActivityPub\ActivityEventHandlers\AcceptHandler; use ActivityPub\ActivityEventHandlers\AddHandler; +use ActivityPub\ActivityEventHandlers\AnnounceHandler; use ActivityPub\ActivityEventHandlers\CreateHandler; use ActivityPub\ActivityEventHandlers\DeleteHandler; use ActivityPub\ActivityEventHandlers\FollowHandler; +use ActivityPub\ActivityEventHandlers\LikeHandler; use ActivityPub\ActivityEventHandlers\NonActivityHandler; +use ActivityPub\ActivityEventHandlers\RemoveHandler; +use ActivityPub\ActivityEventHandlers\UndoHandler; use ActivityPub\ActivityEventHandlers\UpdateHandler; use ActivityPub\ActivityEventHandlers\ValidationHandler; use ActivityPub\Auth\AuthListener; @@ -154,6 +158,24 @@ class ActivityPubModule $this->injector->register( AddHandler::class, AddHandler::class ) ->addArgument( new Reference( ObjectsService::class ) ) ->addArgument( new Reference( CollectionsService::class ) ); + + $this->injector->register( RemoveHandler::class, RemoveHandler::class ) + ->addArgument( new Reference( ObjectsService::class ) ) + ->addArgument( new Reference( CollectionsService::class ) ); + + $this->injector->register( LikeHandler::class, LikeHandler::class ) + ->addArgument( new Reference( ObjectsService::class ) ) + ->addArgument( new Reference( CollectionsService::class ) ) + ->addArgument( new Reference( ContextProvider::class ) ); + + $this->injector->register( AnnounceHandler::class, AnnounceHandler::class ) + ->addArgument( new Reference( ObjectsService::class ) ) + ->addArgument( new Reference( CollectionsService::class ) ) + ->addArgument( new Reference( ContextProvider::class ) ); + + $this->injector->register( UndoHandler::class, UndoHandler::class ) + ->addArgument( new Reference( ObjectsService::class ) ) + ->addArgument( new Reference( CollectionsService::class ) ); } /**