Skip to content

Commit 312bb36

Browse files
fea: (LAR-87) Mise en place des test pour la signalisation d'un contenu (#222)
Co-authored-by: Arthur Monney <monneylobe@gmail.com>
1 parent 69c9c16 commit 312bb36

32 files changed

+519
-107
lines changed

app/Actions/ReportSpamAction.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Actions;
6+
7+
use App\Contracts\SpamReportableContract;
8+
use App\Exceptions\CanReportSpamException;
9+
use App\Models\User;
10+
use App\Notifications\ReportedSpamToTelegram;
11+
12+
final class ReportSpamAction
13+
{
14+
public function execute(User $user, SpamReportableContract $model, ?string $content = null): void
15+
{
16+
if ($model->spamReports()->whereBelongsTo($user)->exists()) {
17+
throw new CanReportSpamException(
18+
message: __('notifications.exceptions.spam_exist'),
19+
);
20+
}
21+
22+
$spam = $model->spamReports()->create([
23+
'user_id' => $user->id,
24+
'reason' => $content,
25+
]);
26+
27+
$user->notify(new ReportedSpamToTelegram($spam));
28+
}
29+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Contracts;
6+
7+
use Illuminate\Database\Eloquent\Relations\MorphMany;
8+
9+
interface SpamReportableContract
10+
{
11+
public function getPathUrl(): string;
12+
13+
public function spamReports(): MorphMany;
14+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Exceptions;
6+
7+
use Exception;
8+
9+
final class CanReportSpamException extends Exception {}

app/Livewire/ReportSpam.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Livewire;
6+
7+
use App\Actions\ReportSpamAction;
8+
use App\Contracts\SpamReportableContract;
9+
use App\Exceptions\CanReportSpamException;
10+
use Filament\Actions\Action;
11+
use Filament\Actions\Concerns\InteractsWithActions;
12+
use Filament\Actions\Contracts\HasActions;
13+
use Filament\Forms\Concerns\InteractsWithForms;
14+
use Filament\Forms\Contracts\HasForms;
15+
use Filament\Notifications\Notification;
16+
use Illuminate\Contracts\View\View;
17+
use Illuminate\Support\Facades\Auth;
18+
use Livewire\Component;
19+
20+
final class ReportSpam extends Component implements HasActions, HasForms
21+
{
22+
use InteractsWithActions;
23+
use InteractsWithForms;
24+
25+
public SpamReportableContract $model;
26+
27+
public ?string $reason = null;
28+
29+
public function reportAction(): Action
30+
{
31+
return Action::make('report')
32+
->color('danger')
33+
->badge()
34+
->label(__('pages/forum.report_spam'))
35+
->authorize('report', $this->model) // @phpstan-ignore-line
36+
->requiresConfirmation()
37+
->action(function (): void {
38+
try {
39+
app(ReportSpamAction::class)->execute(
40+
user: Auth::user(), // @phpstan-ignore-line
41+
model: $this->model,
42+
content: $this->reason,
43+
);
44+
45+
Notification::make()
46+
->title(__('notifications.spam_send'))
47+
->success()
48+
->duration(3500)
49+
->send();
50+
51+
$this->reset('reason');
52+
} catch (CanReportSpamException $e) {
53+
Notification::make()
54+
->title($e->getMessage())
55+
->danger()
56+
->duration(3500)
57+
->send();
58+
}
59+
});
60+
}
61+
62+
public function render(): View
63+
{
64+
return view('livewire.components.report-spam');
65+
}
66+
}

app/Models/Discussion.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66

77
use App\Contracts\ReactableInterface;
88
use App\Contracts\ReplyInterface;
9+
use App\Contracts\SpamReportableContract;
910
use App\Contracts\SubscribeInterface;
1011
use App\Models\Builders\DiscussionQueryBuilder;
1112
use App\Traits\HasAuthor;
1213
use App\Traits\HasReplies;
1314
use App\Traits\HasSlug;
15+
use App\Traits\HasSpamReports;
1416
use App\Traits\HasSubscribers;
1517
use App\Traits\HasTags;
1618
use App\Traits\Reactable;
@@ -21,6 +23,7 @@
2123
use Illuminate\Database\Eloquent\Casts\Attribute;
2224
use Illuminate\Database\Eloquent\Factories\HasFactory;
2325
use Illuminate\Database\Eloquent\Model;
26+
use Illuminate\Support\Collection;
2427
use Illuminate\Support\Str;
2528

2629
/**
@@ -35,13 +38,15 @@
3538
* @property Carbon $created_at
3639
* @property Carbon $updated_at
3740
* @property User $user
41+
* @property Collection | SpamReport[] $spamReports
3842
*/
39-
final class Discussion extends Model implements ReactableInterface, ReplyInterface, SubscribeInterface, Viewable
43+
final class Discussion extends Model implements ReactableInterface, ReplyInterface, SpamReportableContract, SubscribeInterface, Viewable
4044
{
4145
use HasAuthor;
4246
use HasFactory;
4347
use HasReplies;
4448
use HasSlug;
49+
use HasSpamReports;
4550
use HasSubscribers;
4651
use HasTags;
4752
use InteractsWithViews;

app/Models/Reply.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

77
use App\Contracts\ReactableInterface;
88
use App\Contracts\ReplyInterface;
9+
use App\Contracts\SpamReportableContract;
910
use App\Traits\HasAuthor;
1011
use App\Traits\HasReplies;
12+
use App\Traits\HasSpamReports;
1113
use App\Traits\Reactable;
1214
use App\Traits\RecordsActivity;
1315
use Carbon\Carbon;
@@ -17,6 +19,7 @@
1719
use Illuminate\Database\Eloquent\Relations\HasOne;
1820
use Illuminate\Database\Eloquent\Relations\MorphMany;
1921
use Illuminate\Database\Eloquent\Relations\MorphTo;
22+
use Illuminate\Support\Collection;
2023
use Illuminate\Support\Str;
2124

2225
/**
@@ -28,12 +31,14 @@
2831
* @property User $user
2932
* @property int $replyable_id
3033
* @property string $replyable_type
34+
* @property Collection | SpamReport[] $spamReports
3135
*/
32-
final class Reply extends Model implements ReactableInterface, ReplyInterface
36+
final class Reply extends Model implements ReactableInterface, ReplyInterface, SpamReportableContract
3337
{
3438
use HasAuthor;
3539
use HasFactory;
3640
use HasReplies;
41+
use HasSpamReports;
3742
use Reactable;
3843
use RecordsActivity;
3944

@@ -56,11 +61,6 @@ public function getPathUrl(): string
5661
return "#reply-{$this->id}";
5762
}
5863

59-
public function solutionTo(): HasOne
60-
{
61-
return $this->hasOne(Thread::class, 'solution_reply_id');
62-
}
63-
6464
public function wasJustPublished(): bool
6565
{
6666
return $this->created_at->gt(Carbon::now()->subMinute());
@@ -83,6 +83,11 @@ public function to(ReplyInterface $replyable): void
8383
$this->replyAble()->associate($replyable); // @phpstan-ignore-line
8484
}
8585

86+
public function solutionTo(): HasOne
87+
{
88+
return $this->hasOne(Thread::class, 'solution_reply_id');
89+
}
90+
8691
public function allChildReplies(): MorphMany
8792
{
8893
return $this->replies()

app/Models/SpamReport.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Models;
6+
7+
use Illuminate\Database\Eloquent\Factories\HasFactory;
8+
use Illuminate\Database\Eloquent\Model;
9+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
10+
use Illuminate\Database\Eloquent\Relations\MorphTo;
11+
12+
/**
13+
* @property-read int $id
14+
* @property int $user_id
15+
* @property int $reportable_id
16+
* @property string $reportable_type
17+
* @property string | null $reason
18+
* @property User | null $user
19+
*/
20+
final class SpamReport extends Model
21+
{
22+
use HasFactory;
23+
24+
protected $fillable = [
25+
'user_id',
26+
'reportable_id',
27+
'reportable_type',
28+
'reason',
29+
];
30+
31+
public function user(): BelongsTo
32+
{
33+
return $this->belongsTo(User::class);
34+
}
35+
36+
public function reportable(): MorphTo
37+
{
38+
return $this->morphTo();
39+
}
40+
}

app/Models/Thread.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66

77
use App\Contracts\ReactableInterface;
88
use App\Contracts\ReplyInterface;
9+
use App\Contracts\SpamReportableContract;
910
use App\Contracts\SubscribeInterface;
1011
use App\Exceptions\CouldNotMarkReplyAsSolution;
1112
use App\Filters\Thread\ThreadFilters;
1213
use App\Traits\HasAuthor;
1314
use App\Traits\HasReplies;
1415
use App\Traits\HasSlug;
16+
use App\Traits\HasSpamReports;
1517
use App\Traits\HasSubscribers;
1618
use App\Traits\Reactable;
1719
use App\Traits\RecordsActivity;
@@ -49,12 +51,13 @@
4951
* @property Reply | null $solutionReply
5052
* @property \Illuminate\Database\Eloquent\Collection | Channel[] $channels
5153
*/
52-
final class Thread extends Model implements Feedable, ReactableInterface, ReplyInterface, SubscribeInterface, Viewable
54+
final class Thread extends Model implements Feedable, ReactableInterface, ReplyInterface, SpamReportableContract, SubscribeInterface, Viewable
5355
{
5456
use HasAuthor;
5557
use HasFactory;
5658
use HasReplies;
5759
use HasSlug;
60+
use HasSpamReports;
5861
use HasSubscribers;
5962
use InteractsWithViews;
6063
use Notifiable;

app/Models/User.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public function profile(): array
190190
return [
191191
'name' => $this->name,
192192
'username' => $this->username,
193-
'picture' => (string) $this->profile_photo_url,
193+
'picture' => $this->profile_photo_url,
194194
];
195195
}
196196

@@ -269,6 +269,11 @@ public function transactions(): HasMany
269269
return $this->hasMany(Transaction::class);
270270
}
271271

272+
public function spamReports(): HasMany
273+
{
274+
return $this->hasMany(SpamReport::class, 'user_id');
275+
}
276+
272277
public function deleteThreads(): void
273278
{
274279
// We need to explicitly iterate over the threads and delete them

app/Notifications/ArticleSubmitted.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ private function content(): string
4343
{
4444
$content = "*Nouvel Article Soumis!*\n\n";
4545
$content .= 'Titre: '.$this->article->title."\n";
46-
$content .= 'Par: [@'.$this->article->user?->username.']('.route('profile', $this->article->user?->username).')';
46+
$content .= 'Par: [@'.$this->article->user->username.']('.route('profile', $this->article->user->username).')';
4747

4848
return $content;
4949
}
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\Notifications;
6+
7+
use App\Models\SpamReport;
8+
use Illuminate\Bus\Queueable;
9+
use Illuminate\Notifications\Notification;
10+
use NotificationChannels\Telegram\TelegramChannel;
11+
use NotificationChannels\Telegram\TelegramMessage;
12+
13+
final class ReportedSpamToTelegram extends Notification
14+
{
15+
use Queueable;
16+
17+
public function __construct(public SpamReport $spamReport) {}
18+
19+
public function via(object $notifiable): array
20+
{
21+
return [TelegramChannel::class];
22+
}
23+
24+
public function toTelegram(): TelegramMessage
25+
{
26+
return TelegramMessage::create()
27+
->to('@laravelcm')
28+
->content("{$this->spamReport->user?->name} vient de reporter un contenu spam")
29+
->button('Voir les spams', route('filament.admin.pages.dashboard'));
30+
}
31+
}

app/Policies/DiscussionPolicy.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,9 @@ public function unsubscribe(User $user, Discussion $discussion): bool
5151
{
5252
return $discussion->hasSubscriber($user);
5353
}
54+
55+
public function report(User $user, Discussion $discussion): bool
56+
{
57+
return $user->hasVerifiedEmail() && ! $discussion->isAuthoredBy($user);
58+
}
5459
}

app/Policies/ReplyPolicy.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,9 @@ public function delete(User $user, Reply $reply): bool
3131
{
3232
return $reply->isAuthoredBy($user) || $user->isModerator() || $user->isAdmin();
3333
}
34+
35+
public function report(User $user, Reply $reply): bool
36+
{
37+
return $user->hasVerifiedEmail() && ! $reply->isAuthoredBy($user);
38+
}
3439
}

app/Policies/ThreadPolicy.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,9 @@ public function unsubscribe(User $user, Thread $thread): bool
4141
{
4242
return $thread->hasSubscriber($user);
4343
}
44+
45+
public function report(User $user, Thread $thread): bool
46+
{
47+
return $user->hasVerifiedEmail() && ! $thread->isAuthoredBy($user);
48+
}
4449
}

app/Traits/HasSpamReports.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Traits;
6+
7+
use App\Models\SpamReport;
8+
use Illuminate\Database\Eloquent\Relations\MorphMany;
9+
10+
trait HasSpamReports
11+
{
12+
public function spamReports(): MorphMany
13+
{
14+
return $this->morphMany(SpamReport::class, 'reportable');
15+
}
16+
}

0 commit comments

Comments
 (0)