From 1adffe6d71c976fb5431488538672668bb7dd7d2 Mon Sep 17 00:00:00 2001 From: Xinecraft Date: Sun, 16 Feb 2025 17:13:38 +0530 Subject: [PATCH] Add feature to attach evidence as URL --- app/Http/Controllers/BanWardenController.php | 51 +++-- app/Models/PlayerPunishment.php | 31 ++- ...dence_urls_to_player_punishments_table.php | 28 +++ .../js/Pages/BanWarden/ShowPunishment.vue | 176 ++++++++++++++---- routes/web.php | 4 +- 5 files changed, 241 insertions(+), 49 deletions(-) create mode 100644 database/migrations/2025_02_16_095116_add_evidence_urls_to_player_punishments_table.php diff --git a/app/Http/Controllers/BanWardenController.php b/app/Http/Controllers/BanWardenController.php index 41ee9ffec..0cb8acdc4 100644 --- a/app/Http/Controllers/BanWardenController.php +++ b/app/Http/Controllers/BanWardenController.php @@ -16,6 +16,7 @@ use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Str; class BanWardenController extends Controller { @@ -268,8 +269,7 @@ public function indexAlts(PlayerPunishment $playerPunishment) return $players; } - - public function showEvidence(PlayerPunishment $playerPunishment, $evidence, Request $request) + public function showMediaEvidence(PlayerPunishment $playerPunishment, $evidence, Request $request) { $this->authorize('viewEvidence', $playerPunishment); @@ -289,12 +289,17 @@ public function createEvidence(PlayerPunishment $playerPunishment, Request $requ $evidenceMaxSizeKb = config('minetrax.banwarden.evidence_max_size_kb'); $evidenceMaxCount = config('minetrax.banwarden.evidence_max_count'); $request->validate([ + 'type' => 'required|string|in:media,url', 'file' => [ - 'required', + 'required_if:type,media', 'file', 'mimes:' . $evidenceAllowedMimetypes, 'max:' . $evidenceMaxSizeKb, - ] + ], + 'url' => [ + 'required_if:type,url', + 'url', + ], ]); $previousEvidences = $playerPunishment->evidences; @@ -303,22 +308,46 @@ public function createEvidence(PlayerPunishment $playerPunishment, Request $requ ->with(['toast' => ['type' => 'error', 'title' => __('Upload Failed'), 'body' => __('You can only upload up to :count evidence files', ['count' => $evidenceMaxCount])]]); } - $playerPunishment->addMediaFromRequest('file')->toMediaCollection('punishment-evidence'); + if ($request->input('type') === 'media') { + $playerPunishment->addMediaFromRequest('file')->toMediaCollection('punishment-evidence'); + } else { + $playerPunishment->evidence_urls = array_merge($playerPunishment->evidence_urls ?? [], [ + [ + 'id' => Str::uuid(), + 'url' => $request->input('url'), + ] + ]); + $playerPunishment->save(); + } return redirect()->back() ->with(['toast' => ['type' => 'success', 'title' => __('Upload Successful'), 'body' => __('Evidence uploaded successfully')]]); } - public function deleteEvidence(PlayerPunishment $playerPunishment, $evidence) + public function deleteEvidence(PlayerPunishment $playerPunishment, Request $request) { $this->authorize('deleteEvidence', PlayerPunishment::class); - $media = $playerPunishment->getMedia('punishment-evidence')->find($evidence); - if (!$media) { - abort(404); - } + $request->validate([ + 'evidence' => 'required|string', + 'type' => 'required|string|in:media,url', + ]); - $media->delete(); + $evidence = $request->input('evidence'); + $type = $request->input('type'); + + if ($type == 'media') { + $media = $playerPunishment->getMedia('punishment-evidence')->find($evidence); + if (!$media) { + abort(404); + } + $media->delete(); + } else { + $playerPunishment->evidence_urls = array_filter($playerPunishment->evidence_urls, function ($url) use ($evidence) { + return $url['id'] !== $evidence; + }); + $playerPunishment->save(); + } return redirect()->back() ->with(['toast' => ['type' => 'success', 'title' => __('Delete Successful'), 'body' => __('Evidence deleted successfully')]]); diff --git a/app/Models/PlayerPunishment.php b/app/Models/PlayerPunishment.php index ead2813d0..41a727ca9 100644 --- a/app/Models/PlayerPunishment.php +++ b/app/Models/PlayerPunishment.php @@ -20,6 +20,7 @@ class PlayerPunishment extends BaseModel implements HasMedia 'end_at' => 'datetime', 'removed_at' => 'datetime', 'is_ipban' => 'boolean', + 'evidence_urls' => 'array', ]; protected $appends = [ @@ -45,7 +46,35 @@ public function registerMediaCollections(): void public function getEvidencesAttribute() { - return $this->getMedia('punishment-evidence'); + $mediaFiles = $this->getMedia('punishment-evidence'); + $urls = $this->evidence_urls ?? []; + + return collect($mediaFiles)->map(fn($mediaFile) => [ + 'type' => 'media', + 'data' => $mediaFile, + ])->concat( + collect($urls)->map(fn($url) => [ + 'type' => 'url', + 'data' => $url, + ]) + ); + } + + public function scopeEvidences($query, $flag) + { + if ($flag) { + // where has media or evidence_urls is not null + return $query->where(function ($query) { + $query->whereHas('media') + ->orWhereNotNull('evidence_urls'); + }); + } else { + // where has no media and evidence_urls is null + return $query->where(function ($query) { + $query->whereDoesntHave('media') + ->whereNull('evidence_urls'); + }); + } } public function scopeStatus($query, $status) diff --git a/database/migrations/2025_02_16_095116_add_evidence_urls_to_player_punishments_table.php b/database/migrations/2025_02_16_095116_add_evidence_urls_to_player_punishments_table.php new file mode 100644 index 000000000..ada31ced0 --- /dev/null +++ b/database/migrations/2025_02_16_095116_add_evidence_urls_to_player_punishments_table.php @@ -0,0 +1,28 @@ +json('evidence_urls')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('player_punishments', function (Blueprint $table) { + $table->dropColumn('evidence_urls'); + }); + } +}; diff --git a/resources/default/js/Pages/BanWarden/ShowPunishment.vue b/resources/default/js/Pages/BanWarden/ShowPunishment.vue index 199db47e6..720d62137 100644 --- a/resources/default/js/Pages/BanWarden/ShowPunishment.vue +++ b/resources/default/js/Pages/BanWarden/ShowPunishment.vue @@ -13,7 +13,7 @@ import LoadingSpinner from '@/Components/LoadingSpinner.vue'; import JetDialogModal from '@/Jetstream/DialogModal.vue'; import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue'; import { ref } from 'vue'; -import { XCircleIcon } from '@heroicons/vue/24/solid'; +import { LinkIcon, PaperClipIcon, XCircleIcon } from '@heroicons/vue/24/solid'; import XInput from '@/Components/Form/XInput.vue'; import LoadingButton from '@/Components/LoadingButton.vue'; @@ -207,24 +207,45 @@ const altHeaders = [ ]; +const showingUploadDialog = ref(false); const fileRef = ref(null); -const uploadEvidenceForm = useForm({ +const uploadEvidenceAsFileForm = useForm({ + type: 'media', file: null, }); -const triggerEvidenceUploadInput = () => { +const triggerEvidenceAsFileUploadInput = () => { fileRef.value.click(); }; -const handleEvidenceUpload = (event) => { +const handleEvidenceAsFileUpload = (event) => { const file = event.target.files[0]; if (!file) return; - uploadEvidenceForm.file = file; - uploadEvidenceForm.post(route('player.punishment.evidence.store', props.punishment.id)); + uploadEvidenceAsFileForm.clearErrors(); + uploadEvidenceAsUrlForm.clearErrors(); + + uploadEvidenceAsFileForm.file = file; + uploadEvidenceAsFileForm.post(route('player.punishment.evidence.store', props.punishment.id)); }; +const uploadEvidenceAsUrlForm = useForm({ + type: 'url', + url: null, +}); + +const handleEvidenceAsUrlUpload = () => { + uploadEvidenceAsFileForm.clearErrors(); + uploadEvidenceAsUrlForm.clearErrors(); + + uploadEvidenceAsUrlForm.post(route('player.punishment.evidence.store', props.punishment.id), { + onSuccess: () => { + showingUploadDialog.value = false; + }, + }); +} + const showingPardonForm = ref(false); const pardonForm = useForm({ @@ -650,7 +671,7 @@ function reloadPageWithTimeout() {

- {{ __("Uploaded Evidence") }} + {{ __("Attached Evidence") }}

{{ "-" }} @@ -658,21 +679,23 @@ function reloadPageWithTimeout() {

- - -

- {{ uploadEvidenceForm.errors.file }} -

@@ -1428,5 +1433,106 @@ function reloadPageWithTimeout() { + + + + + + + + + diff --git a/routes/web.php b/routes/web.php index 7cc34e901..450c02318 100644 --- a/routes/web.php +++ b/routes/web.php @@ -83,7 +83,7 @@ Route::get('player/punishments/{playerPunishment:id}/history', [\App\Http\Controllers\BanWardenController::class, 'indexLastPunishments'])->name('player.punishment.show.history'); Route::get('player/punishments/{playerPunishment:id}/sessions', [\App\Http\Controllers\BanWardenController::class, 'indexLastSessions'])->name('player.punishment.show.session'); Route::get('player/punishments/{playerPunishment:id}/alts', [\App\Http\Controllers\BanWardenController::class, 'indexAlts'])->name('player.punishment.show.alt'); - Route::get('player/punishments/{playerPunishment:id}/evidence/{evidence}', [\App\Http\Controllers\BanWardenController::class, 'showEvidence'])->name('player.punishment.evidence.show'); + Route::get('player/punishments/{playerPunishment:id}/evidence/{evidence}', [\App\Http\Controllers\BanWardenController::class, 'showMediaEvidence'])->name('player.punishment.evidence.show'); }); /** @@ -151,7 +151,7 @@ // BanWarden (Authenticated) Route::delete('player/punishments/{playerPunishment:id}', [\App\Http\Controllers\BanWardenController::class, 'pardon'])->name('player.punishment.pardon'); Route::post('player/punishments/{playerPunishment:id}/evidence', [\App\Http\Controllers\BanWardenController::class, 'createEvidence'])->name('player.punishment.evidence.store'); - Route::delete('player/punishments/{playerPunishment:id}/evidence/{evidence}', [\App\Http\Controllers\BanWardenController::class, 'deleteEvidence'])->name('player.punishment.evidence.delete'); + Route::delete('player/punishments/{playerPunishment:id}/evidence', [\App\Http\Controllers\BanWardenController::class, 'deleteEvidence'])->name('player.punishment.evidence.delete'); }); /**