Skip to content

Commit dd21471

Browse files
authored
feat: (LAR-86) add bannished sysem (#218)
bannish user unbannished user bannished user can not login send mail when ban or unban user
2 parents 6c2fe5b + d6c731f commit dd21471

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+789
-19
lines changed

app/Actions/User/BanUserAction.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Actions\User;
6+
7+
use App\Events\UserBannedEvent;
8+
use App\Exceptions\CannotBanAdminException;
9+
use App\Exceptions\UserAlreadyBannedException;
10+
use App\Models\User;
11+
12+
final class BanUserAction
13+
{
14+
public function execute(User $user, string $reason): void
15+
{
16+
if ($user->isAdmin() || $user->isModerator()) {
17+
throw new CannotBanAdminException('Impossible de bannir un administrateur.');
18+
}
19+
20+
if ($user->isBanned()) {
21+
throw new UserAlreadyBannedException('Impossible de bannir cet utilisateur car il est déjà banni.');
22+
}
23+
24+
$user->update([
25+
'banned_at' => now(),
26+
'banned_reason' => $reason,
27+
]);
28+
29+
event(new UserBannedEvent($user));
30+
}
31+
}

app/Actions/User/UnBanUserAction.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Actions\User;
6+
7+
use App\Events\UserUnbannedEvent;
8+
use App\Models\User;
9+
10+
final class UnBanUserAction
11+
{
12+
public function execute(User $user): void
13+
{
14+
$user->update([
15+
'banned_at' => null,
16+
'banned_reason' => null,
17+
]);
18+
19+
event(new UserUnbannedEvent($user));
20+
}
21+
}

app/Events/UserBannedEvent.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Events;
6+
7+
use App\Models\User;
8+
use Illuminate\Broadcasting\InteractsWithSockets;
9+
use Illuminate\Foundation\Events\Dispatchable;
10+
use Illuminate\Queue\SerializesModels;
11+
12+
final class UserBannedEvent
13+
{
14+
use Dispatchable, InteractsWithSockets, SerializesModels;
15+
16+
public function __construct(public User $user) {}
17+
}

app/Events/UserUnbannedEvent.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Events;
6+
7+
use App\Models\User;
8+
use Illuminate\Broadcasting\InteractsWithSockets;
9+
use Illuminate\Foundation\Events\Dispatchable;
10+
use Illuminate\Queue\SerializesModels;
11+
12+
final class UserUnbannedEvent
13+
{
14+
use Dispatchable, InteractsWithSockets, SerializesModels;
15+
16+
public function __construct(public User $user) {}
17+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Exceptions;
6+
7+
use Exception;
8+
9+
/**
10+
* @property string $message
11+
*/
12+
final class CannotBanAdminException extends Exception {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Exceptions;
6+
7+
use Exception;
8+
9+
/**
10+
* @property string $message
11+
*/
12+
final class UserAlreadyBannedException extends Exception {}

app/Filament/Resources/UserResource.php

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44

55
namespace App\Filament\Resources;
66

7+
use App\Actions\User\BanUserAction;
8+
use App\Actions\User\UnBanUserAction;
79
use App\Filament\Resources\UserResource\Pages;
810
use App\Models\User;
911
use Awcodes\FilamentBadgeableColumn\Components\Badge;
1012
use Awcodes\FilamentBadgeableColumn\Components\BadgeableColumn;
13+
use Filament\Forms\Components\TextInput;
14+
use Filament\Notifications\Notification;
1115
use Filament\Resources\Resource;
1216
use Filament\Tables;
1317
use Filament\Tables\Table;
@@ -51,21 +55,63 @@ public static function table(Table $table): Table
5155
->icon('untitledui-inbox')
5256
->description(fn ($record): ?string => $record->phone_number),
5357
Tables\Columns\TextColumn::make('email_verified_at')
54-
->label('Validation Email')
58+
->label(__('user.validate_email'))
5559
->placeholder('N/A')
5660
->date(),
5761
Tables\Columns\TextColumn::make(name: 'created_at')
58-
->label('Inscription')
62+
->label(__('use.inscription'))
5963
->date(),
6064
])
6165
->filters([
6266
Tables\Filters\TernaryFilter::make('email_verified_at')
63-
->label('Email Vérifiée')
67+
->label(__('user.email_verified'))
6468
->nullable(),
6569
])
6670
->actions([
67-
Tables\Actions\DeleteAction::make()
68-
->iconButton(),
71+
Tables\Actions\Action::make('ban')
72+
->label(__('actions.ban'))
73+
->icon('untitledui-archive')
74+
->color('warning')
75+
->visible(fn ($record) => $record->banned_at == null)
76+
->modalHeading(__('user.ban.heading'))
77+
->modalDescription(__('user.ban.description'))
78+
->authorize('ban', User::class)
79+
->form([
80+
TextInput::make('banned_reason')
81+
->label(__('user.ban.reason'))
82+
->required(),
83+
])
84+
->action(function (User $record, array $data): void {
85+
app(BanUserAction::class)->execute($record, $data['banned_reason']);
86+
87+
Notification::make()
88+
->success()
89+
->duration(5000)
90+
->title(__('notifications.user.banned_title'))
91+
->body(__('notifications.user.banned_body'))
92+
->send();
93+
})
94+
->requiresConfirmation(),
95+
96+
Tables\Actions\Action::make('unban')
97+
->label(__('actions.unban'))
98+
->icon('heroicon-o-check-circle')
99+
->color('success')
100+
->visible(fn ($record) => $record->banned_at !== null)
101+
->authorize('unban', User::class)
102+
->action(function (User $record): void {
103+
app(UnBanUserAction::class)->execute($record);
104+
105+
Notification::make()
106+
->success()
107+
->title(__('notifications.user.unbanned_title'))
108+
->duration(5000)
109+
->body(__('notifications.user.unbanned_body'))
110+
->send();
111+
})
112+
->requiresConfirmation(),
113+
114+
Tables\Actions\DeleteAction::make(),
69115
])
70116
->bulkActions([
71117
Tables\Actions\DeleteBulkAction::make(),

app/Filament/Resources/UserResource/Pages/ListUsers.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,21 @@
55
namespace App\Filament\Resources\UserResource\Pages;
66

77
use App\Filament\Resources\UserResource;
8+
use Filament\Resources\Components\Tab;
89
use Filament\Resources\Pages\ListRecords;
910

1011
final class ListUsers extends ListRecords
1112
{
1213
protected static string $resource = UserResource::class;
14+
15+
public function getTabs(): array
16+
{
17+
return [
18+
'all' => Tab::make(__('global.all')),
19+
'banned' => Tab::make(__('global.banned'))
20+
->modifyQueryUsing(fn ($query) => $query->isBanned()),
21+
'unbanned' => Tab::make(__('global.unbanned'))
22+
->modifyQueryUsing(fn ($query) => $query->isNotBanned()),
23+
];
24+
}
1325
}

app/Http/Middleware/CheckIfBanned.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Http\Middleware;
6+
7+
use Closure;
8+
use Illuminate\Http\Request;
9+
use Illuminate\Support\Facades\Auth;
10+
use Symfony\Component\HttpFoundation\Response;
11+
12+
final class CheckIfBanned
13+
{
14+
public function handle(Request $request, Closure $next): Response
15+
{
16+
// @phpstan-ignore-next-line
17+
if (Auth::check() && Auth::user()->isBanned()) {
18+
Auth::logout();
19+
20+
return redirect()->route('login')->withErrors([
21+
'email' => __('user.ban.message'),
22+
]);
23+
}
24+
25+
return $next($request);
26+
}
27+
}

app/Jobs/SendBanEmailJob.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Jobs;
6+
7+
use App\Models\User;
8+
use App\Notifications\UserBannedNotification;
9+
use Illuminate\Bus\Queueable;
10+
use Illuminate\Contracts\Queue\ShouldQueue;
11+
use Illuminate\Foundation\Bus\Dispatchable;
12+
use Illuminate\Queue\InteractsWithQueue;
13+
use Illuminate\Queue\SerializesModels;
14+
15+
final class SendBanEmailJob implements ShouldQueue
16+
{
17+
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
18+
19+
public function __construct(public User $user) {}
20+
21+
public function handle(): void
22+
{
23+
$this->user->notify(new UserBannedNotification($this->user));
24+
}
25+
}

app/Jobs/SendUnbanEmailJob.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Jobs;
6+
7+
use App\Models\User;
8+
use App\Notifications\UserUnBannedNotification;
9+
use Illuminate\Bus\Queueable;
10+
use Illuminate\Contracts\Queue\ShouldQueue;
11+
use Illuminate\Foundation\Bus\Dispatchable;
12+
use Illuminate\Queue\InteractsWithQueue;
13+
use Illuminate\Queue\SerializesModels;
14+
15+
final class SendUnbanEmailJob implements ShouldQueue
16+
{
17+
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
18+
19+
public function __construct(public User $user) {}
20+
21+
public function handle(): void
22+
{
23+
$this->user->notify(new UserUnBannedNotification($this->user));
24+
}
25+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Listeners;
6+
7+
use App\Events\UserBannedEvent;
8+
use App\Jobs\SendBanEmailJob;
9+
10+
final class SendBanNotificationListener
11+
{
12+
/**
13+
* Handle the event.
14+
*/
15+
public function handle(UserBannedEvent $event): void
16+
{
17+
SendBanEmailJob::dispatch($event->user);
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Listeners;
6+
7+
use App\Events\UserUnbannedEvent;
8+
use App\Jobs\SendUnbanEmailJob;
9+
10+
final class SendUnbanNotificationListener
11+
{
12+
/**
13+
* Handle the event.
14+
*/
15+
public function handle(UserUnbannedEvent $event): void
16+
{
17+
SendUnbanEmailJob::dispatch($event->user);
18+
}
19+
}

app/Mail/UserBannedEMail.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Mail;
6+
7+
use App\Models\User;
8+
use Illuminate\Bus\Queueable;
9+
use Illuminate\Mail\Mailable;
10+
use Illuminate\Mail\Mailables\Content;
11+
use Illuminate\Mail\Mailables\Envelope;
12+
use Illuminate\Queue\SerializesModels;
13+
14+
final class UserBannedEMail extends Mailable
15+
{
16+
use Queueable, SerializesModels;
17+
18+
public function __construct(public User $user) {}
19+
20+
public function envelope(): Envelope
21+
{
22+
return new Envelope(
23+
subject: __('user.ban.email_subject'),
24+
);
25+
}
26+
27+
public function content(): Content
28+
{
29+
return new Content(
30+
markdown: 'emails.send-banned-message',
31+
);
32+
}
33+
}

0 commit comments

Comments
 (0)