diff --git a/app/Filament/Actions/DeleteLeaderboardAction.php b/app/Filament/Actions/DeleteLeaderboardAction.php new file mode 100644 index 0000000000..69008b3c86 --- /dev/null +++ b/app/Filament/Actions/DeleteLeaderboardAction.php @@ -0,0 +1,38 @@ +label('Delete leaderboard') + ->icon('heroicon-s-trash') + ->color('danger') + ->requiresConfirmation() + ->modalDescription("Are you sure you want to permanently delete this leaderboard?") + ->action(function (Leaderboard $leaderboard) use ($user) { + // TODO use soft deletes + if (!$user->can('forceDelete', $leaderboard)) { + return; + } + + $leaderboard->forceDelete(); + }) + ->visible(function (Leaderboard $leaderboard) use ($user) { + return $user->can('forceDelete', $leaderboard); + }); + } +} diff --git a/app/Filament/Actions/ResetAllLeaderboardEntriesAction.php b/app/Filament/Actions/ResetAllLeaderboardEntriesAction.php new file mode 100644 index 0000000000..8e8de1594e --- /dev/null +++ b/app/Filament/Actions/ResetAllLeaderboardEntriesAction.php @@ -0,0 +1,59 @@ +label('Delete all entries') + ->icon('heroicon-s-trash') + ->color('danger') + ->requiresConfirmation() + ->modalDescription("Are you sure you want to permanently delete all entries of this leaderboard?") + ->action(function ($record = null) use ($user) { + $leaderboard = $record ?? $this->getLeaderboardParent(); + + if (!$user->can('resetAllEntries', $leaderboard)) { + return; + } + + $leaderboard->entries()->delete(); + + activity() + ->useLog('default') + ->causedBy($user->id) + ->performedOn($leaderboard) + ->event('resetAllLeaderboardEntries') + ->log('Reset All Leaderboard Entries'); + }) + ->visible(function () use ($user) { + return $user->can('resetAllEntries', [Leaderboard::class]); + }); + } + + protected function getLeaderboardParent(): ?Leaderboard + { + $livewire = $this->getLivewire(); + + if (!method_exists($livewire, 'getRelationship')) { + return null; + } + + $parentLeaderboardId = $livewire->getRelationship()->getParent()->id; + + return Leaderboard::findOrFail($parentLeaderboardId); + } +} diff --git a/app/Filament/Resources/GameResource/RelationManagers/LeaderboardsRelationManager.php b/app/Filament/Resources/GameResource/RelationManagers/LeaderboardsRelationManager.php index 0f65c51a02..4923c23b5e 100644 --- a/app/Filament/Resources/GameResource/RelationManagers/LeaderboardsRelationManager.php +++ b/app/Filament/Resources/GameResource/RelationManagers/LeaderboardsRelationManager.php @@ -4,6 +4,8 @@ namespace App\Filament\Resources\GameResource\RelationManagers; +use App\Filament\Actions\DeleteLeaderboardAction; +use App\Filament\Actions\ResetAllLeaderboardEntriesAction; use App\Models\Game; use App\Models\Leaderboard; use App\Models\User; @@ -104,59 +106,8 @@ public function table(Table $table): Table return $user->can('update', $leaderboard); }), - Action::make('reset_all_entries') - ->label('Delete All Entries') - ->icon('heroicon-s-trash') - ->color('danger') - ->requiresConfirmation() - ->modalDescription("Are you sure you want to permanently delete all entries of this leaderboard?") - ->action(function (Leaderboard $leaderboard) { - /** @var User $user */ - $user = auth()->user(); - - if (!$user->can('resetAllEntries', $leaderboard)) { - return; - } - - $leaderboard->entries()->delete(); - - activity() - ->useLog('default') - ->causedBy($user) - ->performedOn($leaderboard) - ->event('resetAllLeaderboardEntries') - ->log('Reset All Leaderboard Entries'); - }) - ->visible(function (Leaderboard $leaderboard) { - /** @var User $user */ - $user = auth()->user(); - - return $user->can('resetAllEntries', $leaderboard); - }), - - Action::make('delete_leaderboard') - ->label('Delete Leaderboard') - ->icon('heroicon-s-trash') - ->color('danger') - ->requiresConfirmation() - ->modalDescription("Are you sure you want to permanently delete this leaderboard?") - ->action(function (Leaderboard $leaderboard) { - /** @var User $user */ - $user = auth()->user(); - - // TODO use soft deletes - if (!$user->can('forceDelete', $leaderboard)) { - return; - } - - $leaderboard->forceDelete(); - }) - ->visible(function (Leaderboard $leaderboard) { - /** @var User $user */ - $user = auth()->user(); - - return $user->can('forceDelete', $leaderboard); - }), + ResetAllLeaderboardEntriesAction::make('delete_all_entries'), + DeleteLeaderboardAction::make('delete_leaderboard'), ]), ]) ->bulkActions([ diff --git a/app/Filament/Resources/LeaderboardResource.php b/app/Filament/Resources/LeaderboardResource.php index 1a4e6d0779..665b8f2b92 100644 --- a/app/Filament/Resources/LeaderboardResource.php +++ b/app/Filament/Resources/LeaderboardResource.php @@ -4,6 +4,8 @@ namespace App\Filament\Resources; +use App\Filament\Actions\DeleteLeaderboardAction; +use App\Filament\Actions\ResetAllLeaderboardEntriesAction; use App\Filament\Extensions\Resources\Resource; use App\Filament\Resources\LeaderboardResource\Pages; use App\Filament\Resources\LeaderboardResource\RelationManagers; @@ -90,10 +92,6 @@ public static function infolist(Infolist $infolist): Infolist Infolists\Components\TextEntry::make('Title'), Infolists\Components\TextEntry::make('Description'), - - Infolists\Components\TextEntry::make('LowerIsBetter') - ->label('Lower Is Better') - ->formatStateUsing(fn (string $state): string => $state === '1' ? 'Yes' : 'No'), ]), Infolists\Components\Section::make('Rules') @@ -126,7 +124,12 @@ public static function form(Form $form): Form Forms\Components\TextInput::make('Description') ->maxLength(255), + ]), + Forms\Components\Section::make('Rules') + ->icon('heroicon-c-wrench-screwdriver') + ->columns(['md' => 2, 'xl' => 3, '2xl' => 4]) + ->schema([ Forms\Components\Select::make('Format') ->options( collect(ValueFormat::cases()) @@ -190,6 +193,11 @@ public static function table(Table $table): Table ->orWhere('display_name', 'like', "%{$search}%"); }); }), + + Tables\Columns\TextColumn::make('DisplayOrder') + ->label('Display Order') + ->sortable() + ->toggleable(), ]) ->searchPlaceholder('(ID, Title, Game, Dev)') ->filters([ @@ -230,7 +238,17 @@ public static function table(Table $table): Table }), ]) ->actions([ + Tables\Actions\ActionGroup::make([ + Tables\Actions\ActionGroup::make([ + ResetAllLeaderboardEntriesAction::make('delete_all_entries'), + DeleteLeaderboardAction::make('delete_leaderboard'), + ]) + ->dropdown(false), + Tables\Actions\Action::make('audit-log') + ->url(fn ($record) => LeaderboardResource::getUrl('audit-log', ['record' => $record])) + ->icon('fas-clock-rotate-left'), + ]), ]) ->bulkActions([ @@ -270,4 +288,10 @@ public static function getEloquentQuery(): Builder return parent::getEloquentQuery() ->with(['game', 'developer']); } + + // Do not allow on-site leaderboard creation. + public static function canCreate(): bool + { + return false; + } } diff --git a/app/Filament/Resources/LeaderboardResource/RelationManagers/EntriesRelationManager.php b/app/Filament/Resources/LeaderboardResource/RelationManagers/EntriesRelationManager.php index 7dee4af5be..9d4d0ac90e 100644 --- a/app/Filament/Resources/LeaderboardResource/RelationManagers/EntriesRelationManager.php +++ b/app/Filament/Resources/LeaderboardResource/RelationManagers/EntriesRelationManager.php @@ -4,6 +4,7 @@ namespace App\Filament\Resources\LeaderboardResource\RelationManagers; +use App\Filament\Actions\ResetAllLeaderboardEntriesAction; use App\Models\Leaderboard; use App\Models\LeaderboardEntry; use App\Platform\Actions\RemoveLeaderboardEntry; @@ -70,7 +71,7 @@ public function table(Table $table): Table ]) ->headerActions([ - + ResetAllLeaderboardEntriesAction::make('delete_all_entries'), ]) ->actions([ Tables\Actions\EditAction::make(), diff --git a/app/Helpers/database/leaderboard.php b/app/Helpers/database/leaderboard.php index 08feac5d0f..6eb4555473 100644 --- a/app/Helpers/database/leaderboard.php +++ b/app/Helpers/database/leaderboard.php @@ -240,67 +240,6 @@ function getLeaderboardUserEntry(Leaderboard $leaderboard, User $user): ?array return $retVal; } -function getLeaderboardsList( - int $gameID, - int $sortBy, -): array { - $ifDesc = ""; - if ($sortBy >= 10) { - $ifDesc = " DESC"; - } - - switch ($sortBy % 10) { - case 0: - $orderClause = "ORDER BY ld.DisplayOrder $ifDesc, c.ID, GameTitle"; - break; - case 2: - $orderClause = "ORDER BY GameTitle $ifDesc"; - break; - case 3: - $orderClause = "ORDER BY ConsoleName $ifDesc, c.ID, GameTitle"; - break; - case 4: - $orderClause = "ORDER BY ld.Title $ifDesc"; - break; - case 5: - $orderClause = "ORDER BY ld.Description $ifDesc"; - break; - case 6: - $orderClause = "ORDER BY ld.LowerIsBetter $ifDesc, ld.Format $ifDesc"; - break; - case 7: - $ifDesc = $sortBy == 17 ? "ASC" : "DESC"; - - $orderClause = "ORDER BY NumResults $ifDesc"; - break; - default: - $orderClause = "ORDER BY ld.ID $ifDesc"; - break; - } - - $query = "SELECT - ld.ID, ld.Title, ld.Description, ld.Format, ld.Mem, ld.DisplayOrder, - leInner.NumResults, - ld.LowerIsBetter, ua.User AS Author, - gd.ID AS GameID, gd.ImageIcon AS GameIcon, gd.Title AS GameTitle, - c.Name AS ConsoleName, c.ID AS ConsoleID - FROM LeaderboardDef AS ld - LEFT JOIN GameData AS gd ON gd.ID = ld.GameID - LEFT JOIN - ( - SELECT le.leaderboard_id, COUNT(*) AS NumResults FROM leaderboard_entries AS le - WHERE le.deleted_at IS NULL - GROUP BY le.leaderboard_id - ) AS leInner ON leInner.leaderboard_id = ld.ID - LEFT JOIN Console AS c ON c.ID = gd.ConsoleID - LEFT JOIN UserAccounts AS ua ON ua.ID = ld.author_id - WHERE gd.ID = :gameId - GROUP BY ld.GameID, ld.ID - $orderClause"; - - return legacyDbFetchAll($query, ['gameId' => $gameID])->toArray(); -} - function submitLBData( string $user, int $lbID, @@ -440,25 +379,3 @@ function UploadNewLeaderboard( return true; } - -function requestResetLB(int $lbID): bool -{ - $entries = LeaderboardEntry::where('leaderboard_id', $lbID); - $entriesDeleted = $entries->delete(); - - // When `delete()` returns false, it indicates an error has occurred. - return $entriesDeleted !== false; -} - -function requestDeleteLB(int $lbID): bool -{ - $leaderboard = Leaderboard::find($lbID); - - if (!$leaderboard) { - return false; - } - - $leaderboard->forceDelete(); - - return true; -} diff --git a/app/Policies/LeaderboardPolicy.php b/app/Policies/LeaderboardPolicy.php index 9b47f5e926..a095bfa01e 100644 --- a/app/Policies/LeaderboardPolicy.php +++ b/app/Policies/LeaderboardPolicy.php @@ -78,7 +78,7 @@ public function forceDelete(User $user, Leaderboard $leaderboard): bool ]); } - public function resetAllEntries(User $user, Leaderboard $leaderboard): bool + public function resetAllEntries(User $user): bool { return $user->hasAnyRole([ Role::DEVELOPER_STAFF, diff --git a/config/missing-page-redirector.php b/config/missing-page-redirector.php index adb27724ff..71c44a9186 100644 --- a/config/missing-page-redirector.php +++ b/config/missing-page-redirector.php @@ -88,7 +88,6 @@ * leaderboards */ '/leaderboardinfo.php' => '/leaderboard/{i}', - '/leaderboardList.php' => '/game/{g}/leaderboards', /* * user diff --git a/public/request/leaderboard/delete.php b/public/request/leaderboard/delete.php deleted file mode 100644 index 48551942a8..0000000000 --- a/public/request/leaderboard/delete.php +++ /dev/null @@ -1,21 +0,0 @@ -withErrors(__('legacy.error.permissions')); -} - -$input = Validator::validate(Arr::wrap(request()->post()), [ - 'leaderboard' => 'required|integer|exists:LeaderboardDef,ID', -]); - -$lbId = (int) $input['leaderboard']; - -if (requestDeleteLB($lbId)) { - return back()->with('success', __('legacy.success.delete')); -} - -return back()->withErrors(__('legacy.error.error')); diff --git a/public/request/leaderboard/remove-entry.php b/public/request/leaderboard/remove-entry.php deleted file mode 100644 index 08fb66625d..0000000000 --- a/public/request/leaderboard/remove-entry.php +++ /dev/null @@ -1,40 +0,0 @@ -withErrors(__('legacy.error.permissions')); -} - -$currentUser = User::find($userDetails['ID']); - -$input = Validator::validate(Arr::wrap(request()->post()), [ - 'user' => 'required|string|exists:UserAccounts,User', - 'leaderboard' => 'required|integer|exists:LeaderboardDef,ID', - 'reason' => 'nullable|string|max:200', -]); - -$leaderboardId = (int) $input['leaderboard']; -$targetUser = User::firstWhere('User', $input['user']); -$reason = $input['reason']; - -if (!$targetUser) { - return back()->withErrors(__('legacy.error.error')); -} - -$entry = LeaderboardEntry::where('leaderboard_id', $leaderboardId) - ->where('user_id', $targetUser->id) - ->first(); - -if (!$currentUser->can('delete', $entry)) { - return back()->withErrors(__('legacy.error.permissions')); -} - -(new RemoveLeaderboardEntry())->execute($entry, $reason); - -return back()->with('success', __('legacy.success.ok')); diff --git a/public/request/leaderboard/reset.php b/public/request/leaderboard/reset.php deleted file mode 100644 index d7511e9a18..0000000000 --- a/public/request/leaderboard/reset.php +++ /dev/null @@ -1,23 +0,0 @@ -withErrors(__('legacy.error.permissions')); -} - -$input = Validator::validate(Arr::wrap(request()->post()), [ - 'leaderboard' => 'required|integer|exists:LeaderboardDef,ID', -]); - -$lbId = (int) $input['leaderboard']; - -requestResetLB($lbId); - -$commentText = 'reset all entries for this leaderboard'; -addArticleComment("Server", ArticleType::Leaderboard, $lbId, "\"$user\" $commentText.", $user); - -return back()->with('success', __('legacy.success.ok')); diff --git a/public/request/leaderboard/update.php b/public/request/leaderboard/update.php deleted file mode 100644 index 64d03b8b16..0000000000 --- a/public/request/leaderboard/update.php +++ /dev/null @@ -1,57 +0,0 @@ -post()), [ - 'leaderboard' => 'required|integer', - 'trigger' => 'required', - 'title' => 'required', - 'description' => 'required', - 'format' => 'required', - 'lowerIsBetter' => 'required', - 'order' => 'required|integer', -]); - -$lbID = $input['leaderboard']; -$lbMem = $input['trigger']; -$lbTitle = $input['title']; -$lbDescription = $input['description']; -$lbFormat = $input['format']; -$lbLowerIsBetter = $input['lowerIsBetter']; -$lbDisplayOrder = $input['order']; - -$leaderboard = Leaderboard::find($lbID); -if (!$leaderboard) { - abort(404); -} - -// Only let jr. devs update their own leaderboards -if ($permissions == Permissions::JuniorDeveloper && $leaderboard->developer?->id !== $userDetails['ID']) { - abort(403); -} - -$prevUpdated = $leaderboard->Updated; - -if (submitLBData($user, $lbID, $lbMem, $lbTitle, $lbDescription, $lbFormat, $lbLowerIsBetter, $lbDisplayOrder)) { - // if the leaderboard has entries and it's been at least 10 minutes since the last update, log an audit message - if ($leaderboard->entries()->exists()) { - $leaderboard->refresh(); - - if ($leaderboard->Updated->diffInMinutes($prevUpdated) >= 10) { - $commentText = 'edited this leaderboard'; - addArticleComment("Server", ArticleType::Leaderboard, $lbID, "{$user} {$commentText}.", $user); - } - } - - return response()->json(['message' => __('legacy.success.ok')]); -} - -abort(400); diff --git a/resources/views/pages-legacy/gameInfo.blade.php b/resources/views/pages-legacy/gameInfo.blade.php index c134f600f5..a12931e5e3 100644 --- a/resources/views/pages-legacy/gameInfo.blade.php +++ b/resources/views/pages-legacy/gameInfo.blade.php @@ -527,7 +527,12 @@ function resize() { // Display leaderboard management options depending on if the game has any leaderboards (including hidden) if ($gameModel->leaderboards()->exists()) { - echo "
Manage Leaderboards
"; + $manageLeaderboardsRoute = route('filament.admin.resources.leaderboards.index', [ + 'tableFilters[game][id]' => $gameID, + 'tableSortColumn' => 'DisplayOrder', + 'tableSortDirection' => 'asc', + ]); + echo "
Manage Leaderboards
"; } if ($permissions >= Permissions::Developer) { diff --git a/resources/views/pages-legacy/leaderboardList.blade.php b/resources/views/pages-legacy/leaderboardList.blade.php deleted file mode 100644 index 06519d23fb..0000000000 --- a/resources/views/pages-legacy/leaderboardList.blade.php +++ /dev/null @@ -1,286 +0,0 @@ -keyBy('ID')->map(fn ($system) => $system['Name']); - -if (!authenticateFromCookie($user, $permissions, $userDetails)) { - abort(401); -} - -$offset = requestInputSanitized('o', 0, 'integer'); - -$gameID = requestInputSanitized('g', null, 'integer'); - -// If a game is picked, sort the LBs by DisplayOrder -$sortBy = requestInputSanitized('s', empty($gameID) ? 3 : 0, 'integer'); - -if (empty($gameID)) { - abort(404); -} - -$pageTitle = "Leaderboards - "; - -$gameData = getGameData($gameID); -$codeNotes = []; -if ($permissions >= Permissions::JuniorDeveloper) { - getCodeNotes($gameID, $codeNotes); -} -$pageTitle .= $gameData['Title']; - -$lbData = getLeaderboardsList($gameID, $sortBy); - -if (empty($lbData)) { - abort_with(redirect(route('game.show', $gameID))); -} - -sanitize_outputs( - $requestedConsole, - $gameData['Title'], -); -?> - -= Permissions::JuniorDeveloper): ?> - - -"; -echo ""; - -echo "
"; -echo "

Leaderboards

"; - -if (isset($gameData['ID'])) { - echo "
"; - echo gameAvatar($gameData, iconSize: 64); - echo "
"; -} - -echo ""; - -$sort1 = ($sortBy == 1) ? 11 : 1; -$sort2 = ($sortBy == 2) ? 12 : 2; -$sort3 = ($sortBy == 3) ? 13 : 3; -$sort4 = ($sortBy == 4) ? 14 : 4; -$sort5 = ($sortBy == 5) ? 15 : 5; -$sort6 = ($sortBy == 6) ? 16 : 6; -$sort7 = ($sortBy == 7) ? 17 : 7; - -if ($permissions >= Permissions::JuniorDeveloper) { - echo ""; - echo ""; - echo ""; - echo ""; - echo ""; -} else { - echo ""; - echo ""; - echo ""; - // echo ""; - echo ""; - echo ""; - echo ""; - echo ""; -} - -$listCount = 0; - -$bgColorClassNames = ["!bg-embed", "!bg-box-bg"]; -$currentBgColorIndex = 1; - -foreach ($lbData as $nextLB) { - // Alternate the background color of the achievement rows. - $currentBgColorIndex = $currentBgColorIndex === 1 ? 0 : 1; - - $lbID = $nextLB['ID']; - $lbTitle = attributeEscape($nextLB['Title']); - $lbDesc = attributeEscape($nextLB['Description']); - $lbMem = $nextLB['Mem']; - $lbFormat = $nextLB['Format']; - $lbLowerIsBetter = $nextLB['LowerIsBetter']; - $lbNumEntries = $nextLB['NumResults']; - $lbNumEntries = (int) $lbNumEntries; - $lbDisplayOrder = $nextLB['DisplayOrder']; - $lbAuthor = $nextLB['Author']; - $gameID = $nextLB['GameID']; - $gameTitle = $nextLB['GameTitle']; - $gameIcon = $nextLB['GameIcon']; - $consoleName = $nextLB['ConsoleName']; - - $niceFormat = ($lbLowerIsBetter ? "Smallest " : "Largest ") . (($lbFormat == "SCORE") ? "Score" : "Time"); - - echo ""; - - if ($permissions >= Permissions::JuniorDeveloper) { - // Allow leaderboard edits for devs and jr. devs if they are the author - if ($permissions >= Permissions::Developer || ($lbAuthor == $user && $permissions === Permissions::JuniorDeveloper)) { - $editAllowed = true; - } else { - $editAllowed = false; - } - - echo ""; - - // echo ""; - - // echo ""; - - echo ""; - - echo ""; - - echo ""; - - echo ""; - - echo ""; - - echo ""; - - echo ""; - echo ""; - echo ""; - } else { - echo ""; - - echo ""; - - echo ""; - - // echo ""; - - echo ""; - - echo ""; - - echo ""; - - echo ""; - } - - echo ""; -} - -echo "
IDTitle/DescriptionTypeLower Is BetterDisplay OrderIDGameConsoleTitleDescriptionTypeEntries
"; - echo "$lbID"; - echo ""; - // echo gameAvatar($gameData); - // echo ""; - // echo "$consoleName"; - // echo ""; - echo "
"; - echo ""; - echo "
"; - echo ""; - - // echo ""; - echo ""; - $checked = ($lbLowerIsBetter ? "checked" : ""); - echo ""; - echo ""; - echo ""; - echo "
"; - // echo "Memory:"; - echo ""; - $memStart = ""; - $memCancel = ""; - $memSubmit = ""; - $memValue = ""; - $memChunks = explode("::", $lbMem); - foreach ($memChunks as &$memChunk) { - $part = substr($memChunk, 0, 4); - if ($part == 'STA:') { - $memStart = substr($memChunk, 4); - } elseif ($part == 'CAN:') { - $memCancel = substr($memChunk, 4); - } elseif ($part == 'SUB:') { - $memSubmit = substr($memChunk, 4); - } elseif ($part == 'VAL:') { - $memValue = substr($memChunk, 4); - } - } - - echo ""; - echo ""; - echo ""; - echo ""; - echo ""; - - echo "
"; - - // Only display the entry count for jr. devs - echo "
"; - echo "" . $lbNumEntries . " entries"; - echo "
"; - if ($permissions >= Permissions::Developer) { - if ($lbNumEntries > 0) { - echo "
"; - echo csrf_field(); - echo ""; - echo ""; - echo "
"; - } - echo "
"; - echo csrf_field(); - echo ""; - echo ""; - echo "
"; - } - echo "
"; - if ($editAllowed) { - echo ""; - } - echo "
"; - - echo "
"; - echo "$lbID"; - echo ""; - echo gameAvatar($nextLB, label: false); - echo ""; - echo gameAvatar($nextLB, icon: false); - echo ""; - // echo "$consoleName"; - // echo ""; - echo "$lbTitle"; - echo ""; - echo "$lbDesc"; - echo ""; - echo "$niceFormat"; - echo ""; - echo "$lbNumEntries"; - echo "
"; -echo "
"; -?> -
diff --git a/resources/views/pages-legacy/leaderboardinfo.blade.php b/resources/views/pages-legacy/leaderboardinfo.blade.php index b1df2be1e3..e9b4bdda3a 100644 --- a/resources/views/pages-legacy/leaderboardinfo.blade.php +++ b/resources/views/pages-legacy/leaderboardinfo.blade.php @@ -105,7 +105,12 @@ echo "