diff --git a/lib/Album/AlbumMapper.php b/lib/Album/AlbumMapper.php index 815238b9c..613562e49 100644 --- a/lib/Album/AlbumMapper.php +++ b/lib/Album/AlbumMapper.php @@ -273,6 +273,22 @@ public function removeFile(int $albumId, int $fileId): void { $query->executeStatement(); } + public function removeFilesForUser(int $albumId, string $userId) { + // Remove all photos by this user from the album: + $query = $this->connection->getQueryBuilder(); + $query->delete('photos_albums_files') + ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->eq('owner', $query->createNamedParameter($userId))) + ->executeStatement(); + + // Update the last added photo: + $query = $this->connection->getQueryBuilder(); + $query->update("photos_albums") + ->set('last_added_photo', $query->createNamedParameter($this->getLastAdded($albumId), IQueryBuilder::PARAM_INT)) + ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) + ->executeStatement(); + } + private function getLastAdded(int $albumId): int { $query = $this->connection->getQueryBuilder(); $query->select("file_id") @@ -428,6 +444,34 @@ function computeKey($c) { $this->connection->commit(); } + /** + * @param string $collaboratorId + * @param int $collaboratorType + * @return AlbumInfo[] + */ + public function getSharedAlbumsForCollaborator(string $collaboratorId, int $collaboratorType): array { + $query = $this->connection->getQueryBuilder(); + $rows = $query + ->select("a.album_id", "name", "user", "location", "created", "last_added_photo") + ->from("photos_albums_collabs", "c") + ->leftJoin("c", "photos_albums", "a", $query->expr()->eq("a.album_id", "c.album_id")) + ->where($query->expr()->eq('collaborator_id', $query->createNamedParameter($collaboratorId))) + ->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter($collaboratorType, IQueryBuilder::PARAM_INT))) + ->executeQuery() + ->fetchAll(); + + return array_map(function (array $row) { + return new AlbumInfo( + (int)$row['album_id'], + $row['user'], + $row['name'].' ('.$row['user'].')', + $row['location'], + (int)$row['created'], + (int)$row['last_added_photo'] + ); + }, $rows); + } + /** * @param string $collaboratorId * @param string $collaboratorsType - The type of the collaborator, either a user or a group. @@ -483,7 +527,6 @@ public function getSharedAlbumsForCollaboratorWithFiles(string $collaboratorId, * @return void */ public function deleteUserFromAlbumCollaboratorsList(string $userId, int $albumId): void { - // TODO: only delete if this was not a group share $query = $this->connection->getQueryBuilder(); $query->delete('photos_albums_collabs') ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) @@ -492,17 +535,20 @@ public function deleteUserFromAlbumCollaboratorsList(string $userId, int $albumI ->executeStatement(); // Remove all photos by this user from the album: - $query = $this->connection->getQueryBuilder(); - $query->delete('photos_albums_files') - ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) - ->andWhere($query->expr()->eq('owner', $query->createNamedParameter($userId))) - ->executeStatement(); + $this->removeFilesForUser($albumId, $userId); + } - // Update the last added photo: + /** + * @param string $groupId + * @param int $albumId + * @return void + */ + public function deleteGroupFromAlbumCollaboratorsList(string $groupId, int $albumId): void { $query = $this->connection->getQueryBuilder(); - $query->update("photos_albums") - ->set('last_added_photo', $query->createNamedParameter($this->getLastAdded($albumId), IQueryBuilder::PARAM_INT)) + $query->delete('photos_albums_collabs') ->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->eq('collaborator_id', $query->createNamedParameter($groupId))) + ->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter(self::TYPE_GROUP, IQueryBuilder::PARAM_INT))) ->executeStatement(); } diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index f3e9fbc37..115079221 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -29,11 +29,15 @@ use OCA\Photos\Listener\SabrePluginAuthInitListener; use OCA\DAV\Connector\Sabre\Principal; use OCA\Photos\Listener\NodeDeletedListener; +use OCA\Photos\Listener\GroupUserRemovedListener; +use OCA\Photos\Listener\GroupDeletedListener; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\Files\Events\Node\NodeDeletedEvent; +use OCP\Group\Events\UserRemovedEvent; +use OCP\Group\Events\GroupDeletedEvent; class Application extends App implements IBootstrap { public const APP_ID = 'photos'; @@ -65,7 +69,13 @@ public function __construct() { public function register(IRegistrationContext $context): void { /** Register $principalBackend for the DAV collection */ $context->registerServiceAlias('principalBackend', Principal::class); + $context->registerEventListener(NodeDeletedEvent::class, NodeDeletedListener::class); + + $context->registerEventListener(UserRemovedEvent::class, GroupUserRemovedListener::class); + + $context->registerEventListener(GroupDeletedEvent::class, GroupDeletedListener::class); + $context->registerEventListener(SabrePluginAuthInitEvent::class, SabrePluginAuthInitListener::class); } diff --git a/lib/Listener/GroupDeletedListener.php b/lib/Listener/GroupDeletedListener.php new file mode 100644 index 000000000..6a2f45c1e --- /dev/null +++ b/lib/Listener/GroupDeletedListener.php @@ -0,0 +1,47 @@ +albumMapper = $albumMapper; + } + + public function handle(Event $event): void { + if (!($event instanceof GroupDeletedEvent)) { + return; + } + + // Get all shared albums for this group: + $albums_group = $this->albumMapper->getSharedAlbumsForCollaborator($event->getGroup()->getGID(), AlbumMapper::TYPE_GROUP); + + // Get all users of this group: + $users = $event->getGroup()->getUsers(); + + foreach ($users as $user) { + $uid = $user->getUID(); + + // Get all albums shared with this specific user: + $albums_user = $this->albumMapper->getSharedAlbumsForCollaborator($user->getUID(), AlbumMapper::TYPE_USER); + + // Get all group-shared albums that are not directly shared with the removed user in addition + $albums = array_udiff($albums_group, $albums_user, fn ($a, $b) => ($a->getId() - $b->getId())); + + // Remove their photos from theses albums: + foreach ($albums as $album) { + $this->albumMapper->removeFilesForUser($album->getId(), $user->getUID()); + } + } + + foreach ($albums_group as $album) { + $this->albumMapper->deleteGroupFromAlbumCollaboratorsList($event->getGroup()->getGID(), $album->getId()); + } + } +} diff --git a/lib/Listener/GroupUserRemovedListener.php b/lib/Listener/GroupUserRemovedListener.php new file mode 100644 index 000000000..141b55166 --- /dev/null +++ b/lib/Listener/GroupUserRemovedListener.php @@ -0,0 +1,34 @@ +albumMapper = $albumMapper; + } + + public function handle(Event $event): void { + if (!($event instanceof UserRemovedEvent)) { + return; + } + + // Get all shared albums for this group: + $albums_group = $this->albumMapper->getSharedAlbumsForCollaborator($event->getGroup()->getGID(), AlbumMapper::TYPE_GROUP); + // Get all albums shared with this specific user: + $albums_user = $this->albumMapper->getSharedAlbumsForCollaborator($event->getUser()->getUID(), AlbumMapper::TYPE_USER); + // Get all group-shared albums that are not directly shared with the removed user in addition + $albums = array_udiff($albums_group, $albums_user, fn ($a, $b) => ($a->getId() - $b->getId())); + + // Remove their photos from theses albums: + foreach ($albums as $album) { + $this->albumMapper->removeFilesForUser($album->getId(), $event->getUser()->getUID()); + } + } +}