Add ability to delete comments by moderators
This commit is contained in:
@@ -17,11 +17,13 @@ class IsModerator
|
|||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
if (Auth::check() && Auth::user()->hasRole(UserRole::MODERATOR)) {
|
if (Auth::check() && (
|
||||||
|
Auth::user()->hasRole(UserRole::MODERATOR) ||
|
||||||
|
Auth::user()->hasRole(UserRole::ADMINISTRATOR))) {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
session()->flash('error_msg', 'This resource is restricted to Administrators!');
|
session()->flash('error_msg', 'This resource is restricted to Moderators!');
|
||||||
|
|
||||||
return redirect()->route('home.index');
|
return redirect()->route('home.index');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use App\Enums\UserRole;
|
||||||
use App\Models\Episode;
|
use App\Models\Episode;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Notifications\CommentNotification;
|
use App\Notifications\CommentNotification;
|
||||||
@@ -43,7 +44,7 @@ class Comment extends Component
|
|||||||
'replyState.body' => 'reply',
|
'replyState.body' => 'reply',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function updatedIsEditing($isEditing)
|
public function updatedIsEditing(bool $isEditing)
|
||||||
{
|
{
|
||||||
if (! $isEditing) {
|
if (! $isEditing) {
|
||||||
return;
|
return;
|
||||||
@@ -67,6 +68,15 @@ class Comment extends Component
|
|||||||
{
|
{
|
||||||
$this->authorize('destroy', $this->comment);
|
$this->authorize('destroy', $this->comment);
|
||||||
|
|
||||||
|
$user = Auth::user();
|
||||||
|
|
||||||
|
if ($user->hasRole(UserRole::ADMINISTRATOR) || $user->hasRole(UserRole::MODERATOR)) {
|
||||||
|
$this->comment->deleted_by_moderator_id = $user->id;
|
||||||
|
$this->comment->save();
|
||||||
|
$this->dispatch('refresh');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->comment->delete();
|
$this->comment->delete();
|
||||||
|
|
||||||
$this->dispatch('refresh');
|
$this->dispatch('refresh');
|
||||||
|
|||||||
@@ -72,4 +72,12 @@ class Comment extends Model
|
|||||||
{
|
{
|
||||||
return cache()->remember('commentLikes'.$this->id, 300, fn () => $this->likes->count());
|
return cache()->remember('commentLikes'.$this->id, 300, fn () => $this->likes->count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns wether or not comment has been removed by moderation
|
||||||
|
*/
|
||||||
|
public function isDeletedByModerator(): bool
|
||||||
|
{
|
||||||
|
return $this->deleted_by_moderator_id !== null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Policies;
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Enums\UserRole;
|
||||||
use App\Models\Comment;
|
use App\Models\Comment;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||||
@@ -17,6 +18,11 @@ class CommentPolicy
|
|||||||
|
|
||||||
public function destroy(User $user, Comment $comment): bool
|
public function destroy(User $user, Comment $comment): bool
|
||||||
{
|
{
|
||||||
|
if ($user->hasRole(UserRole::ADMINISTRATOR) ||
|
||||||
|
$user->hasRole(UserRole::MODERATOR)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return $user->id === $comment->user_id;
|
return $user->id === $comment->user_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('comments', function (Blueprint $table) {
|
||||||
|
$table->bigInteger('deleted_by_moderator_id')
|
||||||
|
->nullable()
|
||||||
|
->after('parent_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('comments', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('deleted_by_moderator_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,15 +1,19 @@
|
|||||||
@auth
|
@auth
|
||||||
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR) || Auth::user()->hasRole(\App\Enums\UserRole::MODERATOR))
|
||||||
<div class="relative p-5 bg-white dark:bg-neutral-700/40 rounded-lg overflow-hidden z-10">
|
<div class="relative p-5 bg-white dark:bg-neutral-700/40 rounded-lg overflow-hidden z-10">
|
||||||
|
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
<div class="float-left">
|
<div class="float-left">
|
||||||
<a data-te-toggle="modal" data-te-target="#modalUploadEpisode" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
|
<a data-te-toggle="modal" data-te-target="#modalUploadEpisode" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
|
||||||
<i class="fa-solid fa-plus pr-[6px]"></i> Add Episode
|
<i class="fa-solid fa-plus pr-[6px]"></i> Add Episode
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@endif
|
||||||
<div class="float-right">
|
<div class="float-right">
|
||||||
|
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
<a data-te-toggle="modal" data-te-target="#modalAddSubtitles" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
|
<a data-te-toggle="modal" data-te-target="#modalAddSubtitles" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
|
||||||
<i class="fa-solid fa-plus pr-[6px]"></i> Add Subtitles
|
<i class="fa-solid fa-plus pr-[6px]"></i> Add Subtitles
|
||||||
</a>
|
</a>
|
||||||
|
@endif
|
||||||
<a data-te-toggle="modal" data-te-target="#modalEditEpisode" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
|
<a data-te-toggle="modal" data-te-target="#modalEditEpisode" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
|
||||||
<i class="fa-solid fa-pen pr-[6px]"></i> Edit Episode
|
<i class="fa-solid fa-pen pr-[6px]"></i> Edit Episode
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,40 +1,62 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="flex" id="comment-{{ $comment->id }}">
|
<div class="flex" id="comment-{{ $comment->id }}">
|
||||||
<div class="flex-shrink-0 mr-4">
|
<div class="flex-shrink-0 mr-4">
|
||||||
|
@if($comment->isDeletedByModerator())
|
||||||
|
<img class="h-10 w-10 rounded-full" src="{{ asset('images/default-avatar.webp') }}" alt="Deleted comment">
|
||||||
|
@else
|
||||||
<img class="h-10 w-10 rounded-full" src="{{ $comment->user->getAvatar() }}" alt="{{ $comment->user->name }}">
|
<img class="h-10 w-10 rounded-full" src="{{ $comment->user->getAvatar() }}" alt="{{ $comment->user->name }}">
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow">
|
<div class="flex-grow">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
|
@if($comment->isDeletedByModerator())
|
||||||
|
@if (Auth::check() && (Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR) || Auth::user()->hasRole(\App\Enums\UserRole::MODERATOR)))
|
||||||
|
<p class="font-medium text-gray-900 dark:text-gray-100">Deleted ({{ $comment->user->name }})</p>
|
||||||
|
@else
|
||||||
|
<p class="font-medium text-gray-900 dark:text-gray-100">Deleted</p>
|
||||||
|
@endif
|
||||||
|
@else
|
||||||
<p class="font-medium text-gray-900 dark:text-gray-100">{{ $comment->user->name }}</p>
|
<p class="font-medium text-gray-900 dark:text-gray-100">{{ $comment->user->name }}</p>
|
||||||
|
@endif
|
||||||
@if($comment->user->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
@if($comment->user->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
<a data-te-toggle="tooltip" title="Admin"><i class="fa-solid fa-crown text-yellow-600"></i></a>
|
<a data-te-toggle="tooltip" title="Admin"><i class="fa-solid fa-crown text-yellow-600"></i></a>
|
||||||
@endif
|
@endif
|
||||||
|
@if($comment->user->hasRole(\App\Enums\UserRole::MODERATOR))
|
||||||
|
<a data-te-toggle="tooltip" title="Admin" class="text-rose-600">Moderator</a>
|
||||||
|
@endif
|
||||||
@if($comment->user->hasRole(\App\Enums\UserRole::SUPPORTER))
|
@if($comment->user->hasRole(\App\Enums\UserRole::SUPPORTER))
|
||||||
<a data-te-toggle="tooltip" title="Badge of appreciation for the horny people supporting us! :3"><i class="fa-solid fa-hand-holding-heart text-rose-600"></i></a>
|
<a data-te-toggle="tooltip" title="Badge of appreciation for the horny people supporting us! :3"><i class="fa-solid fa-hand-holding-heart text-rose-600"></i></a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1 flex-grow w-full">
|
<div class="mt-1 flex-grow w-full">
|
||||||
@if ($isEditing)
|
@if($comment->isDeletedByModerator())
|
||||||
<form wire:submit.prevent="editComment">
|
<div class="text-gray-700 dark:text-gray-200">Deleted by moderation.</div>
|
||||||
<div>
|
@if (Auth::check() && (Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR) || Auth::user()->hasRole(\App\Enums\UserRole::MODERATOR)))
|
||||||
<label for="comment" class="sr-only">Comment body</label>
|
<div class="text-gray-700 dark:text-gray-300 pt-1">Original comment: {!! $comment->presenter()->markdownBody() !!}</div>
|
||||||
<textarea id="comment" name="comment" rows="3"
|
@endif
|
||||||
class="bg-white dark:bg-neutral-700 shadow-sm block w-full focus:ring-rose-500 focus:border-rose-500 border-gray-300 dark:border-gray-400/40 text-gray-900 dark:text-gray-200 placeholder:text-gray-400 rounded-md
|
|
||||||
@error('editState.body') border-red-500 @enderror"
|
|
||||||
placeholder="Write something" wire:model.defer="editState.body"></textarea>
|
|
||||||
@error('editState.body')
|
|
||||||
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
|
||||||
@enderror
|
|
||||||
</div>
|
|
||||||
<div class="mt-3 flex items-center justify-between">
|
|
||||||
<button type="submit"
|
|
||||||
class="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md shadow-sm text-white bg-rose-600 hover:bg-rose-700 focus:outline-none focus:ring-2 focus:ring-rose-500">
|
|
||||||
Edit
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
@else
|
@else
|
||||||
<div class="text-gray-700 dark:text-gray-200">{!! $comment->presenter()->markdownBody() !!}</div>
|
@if ($isEditing)
|
||||||
|
<form wire:submit.prevent="editComment">
|
||||||
|
<div>
|
||||||
|
<label for="comment" class="sr-only">Comment body</label>
|
||||||
|
<textarea id="comment" name="comment" rows="3"
|
||||||
|
class="bg-white dark:bg-neutral-700 shadow-sm block w-full focus:ring-rose-500 focus:border-rose-500 border-gray-300 dark:border-gray-400/40 text-gray-900 dark:text-gray-200 placeholder:text-gray-400 rounded-md
|
||||||
|
@error('editState.body') border-red-500 @enderror"
|
||||||
|
placeholder="Write something" wire:model.defer="editState.body"></textarea>
|
||||||
|
@error('editState.body')
|
||||||
|
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
<div class="mt-3 flex items-center justify-between">
|
||||||
|
<button type="submit"
|
||||||
|
class="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md shadow-sm text-white bg-rose-600 hover:bg-rose-700 focus:outline-none focus:ring-2 focus:ring-rose-500">
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
@else
|
||||||
|
<div class="text-gray-700 dark:text-gray-200">{!! $comment->presenter()->markdownBody() !!}</div>
|
||||||
|
@endif
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2 space-x-2 flex flex-row">
|
<div class="mt-2 space-x-2 flex flex-row">
|
||||||
|
|||||||
@@ -46,11 +46,16 @@
|
|||||||
@include('modals.share')
|
@include('modals.share')
|
||||||
|
|
||||||
@auth
|
@auth
|
||||||
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
|
||||||
@include('admin.modals.upload-episode')
|
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR) || Auth::user()->hasRole(\App\Enums\UserRole::MODERATOR))
|
||||||
@include('admin.modals.add-subtitles')
|
@include('admin.modals.edit-episode')
|
||||||
@include('admin.modals.edit-episode')
|
@endif
|
||||||
@endif
|
|
||||||
|
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
|
@include('admin.modals.upload-episode')
|
||||||
|
@include('admin.modals.add-subtitles')
|
||||||
|
@endif
|
||||||
|
|
||||||
@endauth
|
@endauth
|
||||||
<!-- Player Script -->
|
<!-- Player Script -->
|
||||||
@vite(['resources/js/player.js'])
|
@vite(['resources/js/player.js'])
|
||||||
|
|||||||
@@ -51,12 +51,19 @@ Route::group(['middleware' => ['auth', 'auth.admin']], function () {
|
|||||||
Route::get('/admin/tags', [AdminApiController::class, 'getTags'])->name('admin.tags');
|
Route::get('/admin/tags', [AdminApiController::class, 'getTags'])->name('admin.tags');
|
||||||
Route::get('/admin/studios', [AdminApiController::class, 'getStudios'])->name('admin.studios');
|
Route::get('/admin/studios', [AdminApiController::class, 'getStudios'])->name('admin.studios');
|
||||||
|
|
||||||
// Get Tags for editing Episode
|
|
||||||
Route::get('/admin/tags/{episode_id}', [AdminApiController::class, 'getEpisodeTags'])->name('admin.tags.episode');
|
|
||||||
Route::get('/admin/studio/{episode_id}', [AdminApiController::class, 'getEpisodeStudio'])->name('admin.studio.episode');
|
|
||||||
|
|
||||||
// Subtitles
|
// Subtitles
|
||||||
Route::get('/admin/subtitles/{episode_id}', [AdminApiController::class, 'getSubtitles'])->name('admin.subtitles');
|
Route::get('/admin/subtitles/{episode_id}', [AdminApiController::class, 'getSubtitles'])->name('admin.subtitles');
|
||||||
Route::post('/admin/add-new-subtitle', [SubtitleController::class, 'store'])->name('admin.add.new.subtitle');
|
Route::post('/admin/add-new-subtitle', [SubtitleController::class, 'store'])->name('admin.add.new.subtitle');
|
||||||
Route::post('/admin/update-subtitles', [SubtitleController::class, 'update'])->name('admin.update.subtitles');
|
Route::post('/admin/update-subtitles', [SubtitleController::class, 'update'])->name('admin.update.subtitles');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
|---------------------------------------------------------------------------------
|
||||||
|
| Moderator Routes
|
||||||
|
|---------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
Route::group(['middleware' => ['auth', 'auth.moderator']], function () {
|
||||||
|
// Get Tags for editing Episode
|
||||||
|
Route::get('/admin/tags/{episode_id}', [AdminApiController::class, 'getEpisodeTags'])->name('admin.tags.episode');
|
||||||
|
Route::get('/admin/studio/{episode_id}', [AdminApiController::class, 'getEpisodeStudio'])->name('admin.studio.episode');
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user