diff --git a/app/Http/Controllers/Admin/UsersController.php b/app/Http/Controllers/Admin/UsersController.php index c24441d22..9474ccb0b 100644 --- a/app/Http/Controllers/Admin/UsersController.php +++ b/app/Http/Controllers/Admin/UsersController.php @@ -7,6 +7,7 @@ use App\Http\Requests\BanRequest; use App\Jobs\BanUser; use App\Jobs\DeleteUser; +use App\Jobs\DeleteUserThreads; use App\Jobs\UnbanUser; use App\Models\User; use App\Policies\UserPolicy; @@ -39,6 +40,10 @@ public function ban(BanRequest $request, User $user): RedirectResponse $this->dispatchSync(new BanUser($user, $request->get('reason'))); + if ($request->willDeleteThreads()) { + $this->dispatchSync(new DeleteUserThreads($user)); + } + $this->success($user->name().' was banned!'); return redirect()->route('profile', $user->username()); @@ -65,4 +70,15 @@ public function delete(User $user): RedirectResponse return redirect()->route('admin.users'); } + + public function deleteThreads(User $user): RedirectResponse + { + $this->authorize(UserPolicy::DELETE, $user); + + $this->dispatchSync(new DeleteUserThreads($user)); + + $this->success($user->name().' threads were deleted!'); + + return redirect()->route('admin.users'); + } } diff --git a/app/Http/Requests/BanRequest.php b/app/Http/Requests/BanRequest.php index 252626d74..1a4b3f3f7 100644 --- a/app/Http/Requests/BanRequest.php +++ b/app/Http/Requests/BanRequest.php @@ -15,6 +15,7 @@ public function rules(): array { return [ 'reason' => 'required|string', + 'delete_threads' => 'boolean', ]; } @@ -22,4 +23,9 @@ public function reason(): string { return $this->get('reason'); } + + public function willDeleteThreads(): bool + { + return $this->boolean('delete_threads'); + } } diff --git a/app/Jobs/DeleteUserThreads.php b/app/Jobs/DeleteUserThreads.php new file mode 100644 index 000000000..445b9dc4f --- /dev/null +++ b/app/Jobs/DeleteUserThreads.php @@ -0,0 +1,15 @@ +user->deleteThreads(); + } +} diff --git a/resources/views/admin/users.blade.php b/resources/views/admin/users.blade.php index 72c104266..2ab53e2ee 100644 --- a/resources/views/admin/users.blade.php +++ b/resources/views/admin/users.blade.php @@ -68,7 +68,7 @@ {{ $user->createdAt()->format('j M Y H:i:s') }} - + @@ -81,6 +81,14 @@

Deleting this user will remove their account and any related content like threads & replies. This cannot be undone.

+ + + + +

All the threads from this user will be deleted. This cannot be undone.

+
@endcan
diff --git a/resources/views/users/profile.blade.php b/resources/views/users/profile.blade.php index d5c60afff..c77b29a27 100644 --- a/resources/views/users/profile.blade.php +++ b/resources/views/users/profile.blade.php @@ -238,8 +238,11 @@ class="w-full bg-center bg-gray-800 h-60 container mx-auto" type="update" >

Banning this user will prevent them from logging in, posting threads and replying to threads.

-
+
+ + Delete threads +
@endif diff --git a/routes/web.php b/routes/web.php index d5146396c..3e141b0bb 100644 --- a/routes/web.php +++ b/routes/web.php @@ -138,6 +138,8 @@ Route::put('users/{username}/unban', [UsersController::class, 'unban'])->name('.users.unban'); Route::delete('users/{username}', [UsersController::class, 'delete'])->name('.users.delete'); + Route::delete('users/{username}/threads', [UsersController::class, 'deleteThreads'])->name('.users.threads.delete'); + // Articles Route::put('articles/{article}/approve', [AdminArticlesController::class, 'approve'])->name('.articles.approve'); Route::put('articles/{article}/disapprove', [AdminArticlesController::class, 'disapprove'])->name('.articles.disapprove'); diff --git a/tests/Feature/AdminTest.php b/tests/Feature/AdminTest.php index c7b748733..bac4da402 100644 --- a/tests/Feature/AdminTest.php +++ b/tests/Feature/AdminTest.php @@ -41,12 +41,24 @@ assertCanBanUsers(); }); +test('admins can ban a user and delete their threads', function () { + $this->loginAsAdmin(); + + assertCanBanUsersAndDeleteThreads(); +}); + test('moderators can ban a user', function () { $this->loginAsModerator(); assertCanBanUsers(); }); +test('moderators can ban a user and delete their threads', function () { + $this->loginAsModerator(); + + assertCanBanUsersAndDeleteThreads(); +}); + test('admins can unban a user', function () { $this->loginAsAdmin(); @@ -366,11 +378,23 @@ function assertCanBanUsers() { $user = User::factory()->create(['name' => 'Freek Murze']); - test()->put('/admin/users/'.$user->username().'/ban', ['reason' => 'A good reason']) + test()->put('/admin/users/'.$user->username().'/ban', ['reason' => 'A good reason', 'delete_threads' => false]) + ->assertRedirect('/user/'.$user->username()); + + test()->assertDatabaseMissing('users', ['id' => $user->id(), 'banned_at' => null]); + test()->assertDatabaseHas('users', ['id' => $user->id(), 'banned_reason' => 'A good reason']); +} + +function assertCanBanUsersAndDeleteThreads() +{ + $user = User::factory()->create(['name' => 'Freek Murze']); + + test()->put('/admin/users/'.$user->username().'/ban', ['reason' => 'A good reason', 'delete_threads' => true]) ->assertRedirect('/user/'.$user->username()); test()->assertDatabaseMissing('users', ['id' => $user->id(), 'banned_at' => null]); test()->assertDatabaseHas('users', ['id' => $user->id(), 'banned_reason' => 'A good reason']); + test()->assertDatabaseMissing('threads', ['author_id' => $user->id()]); } function assertCanUnbanUsers() @@ -397,6 +421,6 @@ function assertCannotBanUsersByType(int $type) { $user = User::factory()->create(['type' => $type]); - test()->put('/admin/users/'.$user->username().'/ban', ['reason' => 'A good reason']) + test()->put('/admin/users/'.$user->username().'/ban', ['reason' => 'A good reason', 'delete_threads' => fake()->boolean()]) ->assertForbidden(); } diff --git a/tests/Integration/Jobs/DeleteUserThreadsTest.php b/tests/Integration/Jobs/DeleteUserThreadsTest.php new file mode 100644 index 000000000..bc6907773 --- /dev/null +++ b/tests/Integration/Jobs/DeleteUserThreadsTest.php @@ -0,0 +1,21 @@ +create(); + + Thread::factory()->for($user, 'authorRelation')->count(5)->create(); + + $this->loginAsAdmin(); + $this->dispatch(new DeleteUserThreads($user)); + + $this->assertDatabaseMissing('threads', ['author_id' => $user->id()]); +});