diff --git a/routes/teams.php b/routes/teams.php index 7d5e25c8..622ad1db 100644 --- a/routes/teams.php +++ b/routes/teams.php @@ -1,9 +1,9 @@ attributes['deleted_at']) { + if (! $this->attributes['deleted_at']) { return null; } @@ -222,13 +224,24 @@ class Folder extends Model return $this->hasOne(Share::class, 'item_id', 'id'); } + public function teamInvitations(): HasMany + { + return $this->hasMany(TeamFolderInvitation::class, 'folder_id', 'id'); + } + + public function teamMembers(): BelongsToMany + { + return $this->belongsToMany(User::class, 'team_folder_members', 'folder_id', 'user_id') + ->withPivot('permission'); + } + // Delete all folder children public static function boot() { parent::boot(); static::creating(function ($model) { - $model->id = (string)Str::uuid(); + $model->id = (string) Str::uuid(); }); static::deleting(function ($item) { diff --git a/src/Domain/Teams/Actions/InviteMembersIntoTeamFolderAction.php b/src/Domain/Teams/Actions/InviteMembersIntoTeamFolderAction.php index fcf53988..8f51c2fe 100644 --- a/src/Domain/Teams/Actions/InviteMembersIntoTeamFolderAction.php +++ b/src/Domain/Teams/Actions/InviteMembersIntoTeamFolderAction.php @@ -1,12 +1,10 @@ each(function ($member) use ($folder) { - // Create invitation $invitation = TeamFolderInvitation::create([ 'permission' => $member['permission'], @@ -29,4 +26,4 @@ class InviteMembersIntoTeamFolderAction ->notify(new InvitationIntoTeamFolder($folder, $invitation)); }); } -} \ No newline at end of file +} diff --git a/src/Domain/Teams/Actions/UpdateInvitationsAction.php b/src/Domain/Teams/Actions/UpdateInvitationsAction.php new file mode 100644 index 00000000..17694527 --- /dev/null +++ b/src/Domain/Teams/Actions/UpdateInvitationsAction.php @@ -0,0 +1,57 @@ +teamInvitations() + ->pluck('email'); + + // Get newbies added by user in request + $newbies = collect($invitations) + ->filter( + fn ($invitation) => ! in_array($invitation['email'], $storedInvitations->toArray()) + ); + + // Get deleted invitations by user in request + $removed = $storedInvitations->diff( + collect($invitations)->pluck('email')->toArray() + ); + + // Invite team members + if ($newbies->isNotEmpty()) { + ($this->inviteMembers)($newbies->toArray(), $folder); + } + + // Delete invite from team folder + if ($removed->isNotEmpty()) { + DB::table('team_folder_invitations') + ->where('folder_id', $folder->id) + ->whereIn('email', $removed) + ->delete(); + } + + // Update privileges + collect($invitations) + ->each( + fn ($invitation) => + DB::table('team_folder_invitations') + ->where('folder_id', $folder->id) + ->where('email', $invitation['email']) + ->update([ + 'permission' => $invitation['permission'], + ]) + ); + } +} diff --git a/src/Domain/Teams/Actions/UpdateMembersAction.php b/src/Domain/Teams/Actions/UpdateMembersAction.php new file mode 100644 index 00000000..34f52cab --- /dev/null +++ b/src/Domain/Teams/Actions/UpdateMembersAction.php @@ -0,0 +1,40 @@ +teamMembers() + ->pluck('user_id'); + + // Get deleted members from request + $deletedMembers = $existingMembers->diff( + collect($members)->pluck('id')->toArray() + ); + + // Remove team members from team folder + if ($deletedMembers->isNotEmpty()) { + DB::table('team_folder_members') + ->where('folder_id', $folder->id) + ->whereIn('user_id', $deletedMembers->toArray()) + ->delete(); + } + + // Update privileges + collect($members) + ->each( + fn ($member) => + DB::table('team_folder_members') + ->where('folder_id', $folder->id) + ->where('user_id', $member['id']) + ->update([ + 'permission' => $member['permission'], + ]) + ); + } +} diff --git a/src/Domain/Teams/Controllers/BrowseSharedWithMeController.php b/src/Domain/Teams/Controllers/BrowseSharedWithMeController.php index 3c2cae45..fc4b4fc7 100644 --- a/src/Domain/Teams/Controllers/BrowseSharedWithMeController.php +++ b/src/Domain/Teams/Controllers/BrowseSharedWithMeController.php @@ -1,14 +1,11 @@ where('user_id', Auth::id()) ->pluck('folder_id'); @@ -49,4 +45,4 @@ class BrowseSharedWithMeController 'folder' => $requestedFolder, ]; } -} \ No newline at end of file +} diff --git a/src/Domain/Teams/Controllers/ConvertFolderIntoTeamFolderController.php b/src/Domain/Teams/Controllers/ConvertFolderIntoTeamFolderController.php index 1472e5cd..5b1ba857 100644 --- a/src/Domain/Teams/Controllers/ConvertFolderIntoTeamFolderController.php +++ b/src/Domain/Teams/Controllers/ConvertFolderIntoTeamFolderController.php @@ -1,26 +1,23 @@ update([ 'team_folder' => 1, 'parent_id' => null, @@ -31,4 +28,4 @@ class ConvertFolderIntoTeamFolderController extends Controller return response($folder, 201); } -} \ No newline at end of file +} diff --git a/src/Domain/Teams/Controllers/InvitationsController.php b/src/Domain/Teams/Controllers/InvitationsController.php index 955960ae..f075544d 100644 --- a/src/Domain/Teams/Controllers/InvitationsController.php +++ b/src/Domain/Teams/Controllers/InvitationsController.php @@ -1,12 +1,11 @@ insert([ 'folder_id' => $invitation->folder_id, - 'user_id' => $user->id, + 'user_id' => $user->id, 'permission' => 'can-edit', ]); @@ -33,11 +32,10 @@ class InvitationsController extends Controller public function destroy( TeamFolderInvitation $invitation ): Response { - $invitation->update([ 'status' => 'rejected', ]); return response('Done', 204); } -} \ No newline at end of file +} diff --git a/src/Domain/Teams/Controllers/TeamFoldersController.php b/src/Domain/Teams/Controllers/TeamFoldersController.php index de9fbc93..0382300a 100644 --- a/src/Domain/Teams/Controllers/TeamFoldersController.php +++ b/src/Domain/Teams/Controllers/TeamFoldersController.php @@ -1,23 +1,24 @@ where('folder_id', $folder->id) - ->pluck('user_id'); + public function update( + Request $request, + Folder $folder, + UpdateInvitationsAction $updateInvitations, + UpdateMembersAction $updateMembers, + ): Response { + $updateInvitations( + $folder, + $request->input('invitations') + ); - // Get deleted members from request - // TODO: vymazat uzivatela z pozvankou - $deletedMembers = $existingMembers - ->filter(fn ($memberId) => ! in_array( - $memberId, collect($request->input('members'))->pluck('id')->toArray() - )); - - // Get newly added members from request - $newMembers = collect($request->input('members')) - ->filter(fn ($member) => ! Str::isUuid($member['id'])); - - // Invite team members - if ($newMembers->isNotEmpty()) { - ($this->inviteMembers)($newMembers->toArray(), $folder); - } - - // Remove team members from team folder - if ($deletedMembers->isNotEmpty()) { - DB::table('team_folder_members') - ->whereIn('user_id', $deletedMembers->toArray()) - ->delete(); - } - - // Update privileges - collect($request->input('members')) - ->each(function ($member) { - DB::table('team_folder_members') - ->where('user_id', $member['id']) - ->update([ - 'permission' => $member['permission'], - ]); - }); + $updateMembers( + $folder, + $request->input('members') + ); return response('Done', 201); } public function destroy(Folder $folder): Response { - $folder->update([ - 'team_folder' => 0, - ]); - + // Delete existing invitations DB::table('team_folder_invitations') ->where('folder_id', $folder->id) ->delete(); + // Delete attached members from folder DB::table('team_folder_members') ->where('folder_id', $folder->id) ->delete(); + $folder->update([ + 'team_folder' => 0, + ]); + return response('Done.', 204); } } diff --git a/tests/Domain/Teams/TeamsTest.php b/tests/Domain/Teams/TeamsTest.php index 21481c7d..6c336ca9 100644 --- a/tests/Domain/Teams/TeamsTest.php +++ b/tests/Domain/Teams/TeamsTest.php @@ -1,14 +1,13 @@ create(); @@ -194,6 +193,14 @@ class TeamsTest extends TestCase 'team_folder' => 1, ]); + TeamFolderInvitation::factory() + ->create([ + 'folder_id' => $folder->id, + 'status' => 'pending', + 'permission' => 'can-edit', + 'email' => 'existing@member.com', + ]); + DB::table('team_folder_members') ->insert([ [ @@ -211,7 +218,96 @@ class TeamsTest extends TestCase $this ->actingAs($user) ->patchJson("/api/teams/folders/{$folder->id}", [ - 'members' => [ + 'members' => [ + [ + 'id' => $members[0]->id, + 'email' => 'john@internal.com', + 'permission' => 'can-edit', + ], + [ + 'id' => $members[1]->id, + 'email' => 'jane@external.com', + 'permission' => 'can-edit', + ], + ], + 'invitations' => [ + [ + 'id' => null, + 'email' => 'existing@member.com', + 'permission' => 'can-edit', + ], + [ + 'id' => null, + 'email' => 'added@member.com', + 'permission' => 'can-view', + ], + ], + ]) + ->assertCreated(); + + $this + ->assertDatabaseCount('team_folder_members', 2) + ->assertDatabaseCount('team_folder_invitations', 2) + ->assertDatabaseHas('team_folder_invitations', [ + 'email' => 'added@member.com', + 'permission' => 'can-view', + ]); + + Notification::assertTimesSent(1, InvitationIntoTeamFolder::class); + } + + /** + * @test + */ + public function it_delete_invited_member_from_team_folder() + { + $user = User::factory(User::class) + ->create(); + + $members = User::factory(User::class) + ->count(2) + ->create(); + + $folder = Folder::factory() + ->create([ + 'user_id' => $user->id, + 'team_folder' => 1, + ]); + + TeamFolderInvitation::factory() + ->create([ + 'folder_id' => $folder->id, + 'status' => 'pending', + 'permission' => 'can-edit', + 'email' => 'deleted@member.com', + ]); + + TeamFolderInvitation::factory() + ->create([ + 'folder_id' => $folder->id, + 'status' => 'pending', + 'permission' => 'can-edit', + 'email' => 'existing@member.com', + ]); + + DB::table('team_folder_members') + ->insert([ + [ + 'folder_id' => $folder->id, + 'user_id' => $members[0]->id, + 'permission' => 'can-edit', + ], + [ + 'folder_id' => $folder->id, + 'user_id' => $members[1]->id, + 'permission' => 'can-edit', + ], + ]); + + $this + ->actingAs($user) + ->patchJson("/api/teams/folders/{$folder->id}", [ + 'members' => [ [ 'id' => $members[0]->id, 'email' => 'john@internal.com', @@ -222,9 +318,11 @@ class TeamsTest extends TestCase 'email' => 'jane@external.com', 'permission' => 'can-view', ], + ], + 'invitations' => [ [ 'id' => null, - 'email' => 'new@member.com', + 'email' => 'existing@member.com', 'permission' => 'can-view', ], ], @@ -233,12 +331,10 @@ class TeamsTest extends TestCase $this ->assertDatabaseCount('team_folder_members', 2) + ->assertDatabaseCount('team_folder_invitations', 1) ->assertDatabaseHas('team_folder_invitations', [ - 'email' => 'new@member.com', - 'permission' => 'can-view', + 'email' => 'existing@member.com', ]); - - Notification::assertTimesSent(1, InvitationIntoTeamFolder::class); } /** @@ -283,6 +379,7 @@ class TeamsTest extends TestCase 'permission' => 'can-edit', ], ], + 'invitations' => [], ]) ->assertCreated(); @@ -293,6 +390,53 @@ class TeamsTest extends TestCase ]); } + /** + * @test + */ + public function it_update_invited_member_permission_in_team_folder() + { + $user = User::factory(User::class) + ->create(); + + $folder = Folder::factory() + ->create([ + 'user_id' => $user->id, + 'team_folder' => 1, + ]); + + TeamFolderInvitation::factory() + ->create([ + 'folder_id' => $folder->id, + 'status' => 'pending', + 'permission' => 'can-view', + 'email' => 'existing@member.com', + ]); + + $this + ->actingAs($user) + ->patchJson("/api/teams/folders/{$folder->id}", [ + 'members' => [], + 'invitations' => [ + [ + 'id' => null, + 'email' => 'existing@member.com', + 'permission' => 'can-edit', + ], + ], + ]) + ->assertCreated(); + + $this + ->assertDatabaseCount('team_folder_members', 0) + ->assertDatabaseCount('team_folder_invitations', 1) + ->assertDatabaseHas('team_folder_invitations', [ + 'email' => 'existing@member.com', + 'permission' => 'can-edit', + ]); + + Notification::assertTimesSent(0, InvitationIntoTeamFolder::class); + } + /** * @test */ @@ -414,7 +558,7 @@ class TeamsTest extends TestCase $this ->actingAs($user) - ->getJson("/api/teams/folders/undefined") + ->getJson('/api/teams/folders/undefined') ->assertOk() ->assertJsonFragment([ 'id' => $folder->id,