From 4f9f6c63b3427067d9bfe44abb8c89a18028bf1c Mon Sep 17 00:00:00 2001 From: Benjamin Date: Wed, 10 Jun 2026 11:04:26 +0200 Subject: [PATCH] Update Submissions and add fields --- .../Resources/Categories/CategoryResource.php | 48 +++ .../Categories/Pages/CreateCategory.php | 11 + .../Categories/Pages/EditCategory.php | 19 ++ .../Categories/Pages/ListCategories.php | 19 ++ .../Categories/Schemas/CategoryForm.php | 24 ++ .../Categories/Tables/CategoriesTable.php | 42 +++ .../Resources/Levels/LevelResource.php | 48 +++ .../Resources/Levels/Pages/CreateLevel.php | 11 + .../Resources/Levels/Pages/EditLevel.php | 19 ++ .../Resources/Levels/Pages/ListLevels.php | 19 ++ .../Resources/Levels/Schemas/LevelForm.php | 20 ++ .../Resources/Levels/Tables/LevelsTable.php | 42 +++ .../Resources/Systems/Pages/CreateSystem.php | 11 + .../Resources/Systems/Pages/EditSystem.php | 19 ++ .../Resources/Systems/Pages/ListSystems.php | 19 ++ .../Resources/Systems/Schemas/SystemForm.php | 20 ++ .../Resources/Systems/SystemResource.php | 48 +++ .../Resources/Systems/Tables/SystemsTable.php | 42 +++ app/Helpers/FormHelpers.php | 59 +++- app/Http/Controllers/SubmissionController.php | 28 +- app/Http/Requests/StoreEntryRequest.php | 37 ++- app/Livewire/Database.php | 74 ++++- app/Livewire/GameSelector.php | 72 ++++- app/Models/Author.php | 22 ++ app/Models/Category.php | 33 ++ app/Models/Entry.php | 102 +++++- app/Models/EntryFile.php | 33 ++ app/Models/EntryGallery.php | 11 +- app/Models/EntryHash.php | 23 ++ app/Models/Gallery.php | 34 ++ app/Models/Game.php | 24 ++ app/Models/Genre.php | 18 ++ app/Models/Language.php | 18 ++ app/Models/Level.php | 26 ++ app/Models/Modification.php | 16 + app/Models/Platform.php | 22 ++ app/Models/Status.php | 16 + app/Models/System.php | 26 ++ app/Models/User.php | 25 ++ app/Services/SubmissionsService.php | 149 +++++++-- app/Traits/HasGallery.php | 14 + app/View/Components/CategorySelector.php | 41 +++ composer.json | 1 + composer.lock | 301 +++++++++++++++++- ...026_06_09_122425_create_category_table.php | 29 ++ .../2026_06_09_122655_create_os_table.php | 29 ++ .../2026_06_09_122817_create_level_table.php | 29 ++ ..._06_09_123242_add_entry_level_id_field.php | 28 ++ ...6_09_123458_create_entry_systems_table.php | 28 ++ ...9_123533_create_entry_categories_table.php | 28 ++ ...832_add_restricted_field_to_categories.php | 28 ++ ...84936_make_entry_galleries_polymorphic.php | 40 +++ .../2026_06_10_090320_create_news_table.php | 27 ++ extra.less | 2 +- resources/css/components/common.css | 5 + resources/css/components/forms.css | 41 +++ resources/js/submissions.js | 18 +- .../components/category-selector.blade.php | 29 ++ .../views/components/entry-card.blade.php | 19 +- resources/views/entries/show.blade.php | 99 ++++-- resources/views/livewire/database.blade.php | 9 + .../views/livewire/game-selector.blade.php | 168 ++++++---- resources/views/submissions/form.blade.php | 76 +++-- routes/web.php | 14 +- 64 files changed, 2278 insertions(+), 174 deletions(-) create mode 100644 app/Filament/Resources/Categories/CategoryResource.php create mode 100644 app/Filament/Resources/Categories/Pages/CreateCategory.php create mode 100644 app/Filament/Resources/Categories/Pages/EditCategory.php create mode 100644 app/Filament/Resources/Categories/Pages/ListCategories.php create mode 100644 app/Filament/Resources/Categories/Schemas/CategoryForm.php create mode 100644 app/Filament/Resources/Categories/Tables/CategoriesTable.php create mode 100644 app/Filament/Resources/Levels/LevelResource.php create mode 100644 app/Filament/Resources/Levels/Pages/CreateLevel.php create mode 100644 app/Filament/Resources/Levels/Pages/EditLevel.php create mode 100644 app/Filament/Resources/Levels/Pages/ListLevels.php create mode 100644 app/Filament/Resources/Levels/Schemas/LevelForm.php create mode 100644 app/Filament/Resources/Levels/Tables/LevelsTable.php create mode 100644 app/Filament/Resources/Systems/Pages/CreateSystem.php create mode 100644 app/Filament/Resources/Systems/Pages/EditSystem.php create mode 100644 app/Filament/Resources/Systems/Pages/ListSystems.php create mode 100644 app/Filament/Resources/Systems/Schemas/SystemForm.php create mode 100644 app/Filament/Resources/Systems/SystemResource.php create mode 100644 app/Filament/Resources/Systems/Tables/SystemsTable.php create mode 100644 app/Models/Category.php create mode 100644 app/Models/Gallery.php create mode 100644 app/Models/Level.php create mode 100644 app/Models/System.php create mode 100644 app/Traits/HasGallery.php create mode 100644 app/View/Components/CategorySelector.php create mode 100644 database/migrations/2026_06_09_122425_create_category_table.php create mode 100644 database/migrations/2026_06_09_122655_create_os_table.php create mode 100644 database/migrations/2026_06_09_122817_create_level_table.php create mode 100644 database/migrations/2026_06_09_123242_add_entry_level_id_field.php create mode 100644 database/migrations/2026_06_09_123458_create_entry_systems_table.php create mode 100644 database/migrations/2026_06_09_123533_create_entry_categories_table.php create mode 100644 database/migrations/2026_06_09_124832_add_restricted_field_to_categories.php create mode 100644 database/migrations/2026_06_10_084936_make_entry_galleries_polymorphic.php create mode 100644 database/migrations/2026_06_10_090320_create_news_table.php create mode 100644 resources/views/components/category-selector.blade.php diff --git a/app/Filament/Resources/Categories/CategoryResource.php b/app/Filament/Resources/Categories/CategoryResource.php new file mode 100644 index 0000000..2b6fa11 --- /dev/null +++ b/app/Filament/Resources/Categories/CategoryResource.php @@ -0,0 +1,48 @@ + ListCategories::route('/'), + 'create' => CreateCategory::route('/create'), + 'edit' => EditCategory::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/Categories/Pages/CreateCategory.php b/app/Filament/Resources/Categories/Pages/CreateCategory.php new file mode 100644 index 0000000..a842af9 --- /dev/null +++ b/app/Filament/Resources/Categories/Pages/CreateCategory.php @@ -0,0 +1,11 @@ +components([ + TextInput::make('name') + ->required(), + TextInput::make('slug') + ->required(), + Textarea::make('restricted_to') + ->default(null) + ->columnSpanFull(), + ]); + } +} diff --git a/app/Filament/Resources/Categories/Tables/CategoriesTable.php b/app/Filament/Resources/Categories/Tables/CategoriesTable.php new file mode 100644 index 0000000..346d3a9 --- /dev/null +++ b/app/Filament/Resources/Categories/Tables/CategoriesTable.php @@ -0,0 +1,42 @@ +columns([ + TextColumn::make('name') + ->searchable(), + TextColumn::make('slug') + ->searchable(), + TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + // + ]) + ->recordActions([ + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Filament/Resources/Levels/LevelResource.php b/app/Filament/Resources/Levels/LevelResource.php new file mode 100644 index 0000000..c2bb67d --- /dev/null +++ b/app/Filament/Resources/Levels/LevelResource.php @@ -0,0 +1,48 @@ + ListLevels::route('/'), + 'create' => CreateLevel::route('/create'), + 'edit' => EditLevel::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/Levels/Pages/CreateLevel.php b/app/Filament/Resources/Levels/Pages/CreateLevel.php new file mode 100644 index 0000000..e8cfbde --- /dev/null +++ b/app/Filament/Resources/Levels/Pages/CreateLevel.php @@ -0,0 +1,11 @@ +components([ + TextInput::make('name') + ->required(), + TextInput::make('slug') + ->required(), + ]); + } +} diff --git a/app/Filament/Resources/Levels/Tables/LevelsTable.php b/app/Filament/Resources/Levels/Tables/LevelsTable.php new file mode 100644 index 0000000..d64fd20 --- /dev/null +++ b/app/Filament/Resources/Levels/Tables/LevelsTable.php @@ -0,0 +1,42 @@ +columns([ + TextColumn::make('name') + ->searchable(), + TextColumn::make('slug') + ->searchable(), + TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + // + ]) + ->recordActions([ + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Filament/Resources/Systems/Pages/CreateSystem.php b/app/Filament/Resources/Systems/Pages/CreateSystem.php new file mode 100644 index 0000000..c03c9fd --- /dev/null +++ b/app/Filament/Resources/Systems/Pages/CreateSystem.php @@ -0,0 +1,11 @@ +components([ + TextInput::make('name') + ->required(), + TextInput::make('slug') + ->required(), + ]); + } +} diff --git a/app/Filament/Resources/Systems/SystemResource.php b/app/Filament/Resources/Systems/SystemResource.php new file mode 100644 index 0000000..14aead4 --- /dev/null +++ b/app/Filament/Resources/Systems/SystemResource.php @@ -0,0 +1,48 @@ + ListSystems::route('/'), + 'create' => CreateSystem::route('/create'), + 'edit' => EditSystem::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/Systems/Tables/SystemsTable.php b/app/Filament/Resources/Systems/Tables/SystemsTable.php new file mode 100644 index 0000000..822e32e --- /dev/null +++ b/app/Filament/Resources/Systems/Tables/SystemsTable.php @@ -0,0 +1,42 @@ +columns([ + TextColumn::make('name') + ->searchable(), + TextColumn::make('slug') + ->searchable(), + TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + // + ]) + ->recordActions([ + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Helpers/FormHelpers.php b/app/Helpers/FormHelpers.php index b8aac16..efba8c8 100644 --- a/app/Helpers/FormHelpers.php +++ b/app/Helpers/FormHelpers.php @@ -44,7 +44,64 @@ class FormHelpers { 'homebrew' => [ 'page_title' => "Submit an homebrew", 'about_the' => "About the homebrew", - 'version' => "Patch version", + 'version' => "Version", + 'status' => "Status", + 'release_date' => "Release date", + 'release_date_helper' => "If only initial release exist, the release date.", + 'description' => "Description", + 'about_game' => "Game Information", + 'attachments' => "Attachments", + 'authors' => "Team members", + 'related_links' => "Related links", + 'release_site' => "Release site", + 'release_site_helper' => "Project entry on site/blog/forum/Github.", + 'youtube_video' => "YouTube video", + ], + 'utilities' => [ + 'page_title' => "Submit a utility", + 'entry_title' => "Title", + 'about_the' => "About the utility", + 'version' => "Version", + 'status' => "Status", + 'system' => "OS", + 'categories' => "Categories", + 'level' => "Experience Level", + 'release_date' => "Release date", + 'release_date_helper' => "If only initial release exist, the release date.", + 'description' => "Description", + 'about_game' => "Game Information", + 'attachments' => "Attachments", + 'authors' => "Team members", + 'related_links' => "Related links", + 'release_site' => "Release site", + 'release_site_helper' => "Project entry on site/blog/forum/Github.", + 'youtube_video' => "YouTube video", + ], + 'documents' => [ + 'page_title' => "Submit a document", + 'entry_title' => "Title", + 'about_the' => "About the document", + 'version' => "Version", + 'status' => "Status", + 'categories' => "Categories", + 'level' => "Experience Level", + 'release_date' => "Release date", + 'release_date_helper' => "If only initial release exist, the release date.", + 'description' => "Description", + 'about_game' => "Game Information", + 'attachments' => "Attachments", + 'authors' => "Team members", + 'related_links' => "Related links", + 'release_site' => "Release site", + 'release_site_helper' => "Project entry on site/blog/forum/Github.", + 'youtube_video' => "YouTube video", + ], + 'lua-scripts' => [ + 'page_title' => "Submit a LUA Script", + 'about_the' => "About the script", + 'entry_title' => "Title", + 'type_of_hack' => "Modifications", + 'version' => "Version", 'status' => "Status", 'release_date' => "Release date", 'release_date_helper' => "If only initial release exist, the release date.", diff --git a/app/Http/Controllers/SubmissionController.php b/app/Http/Controllers/SubmissionController.php index 2f1f7f1..5d8b4c5 100644 --- a/app/Http/Controllers/SubmissionController.php +++ b/app/Http/Controllers/SubmissionController.php @@ -10,14 +10,16 @@ use App\Jobs\DeleteXenForoCommentsThread; use App\Models\Author; use App\Models\Entry; use App\Models\EntryFile; -use App\Models\EntryGallery; +use App\Models\Gallery; use App\Models\EntryHash; use App\Models\Game; use App\Models\Genre; use App\Models\Language; +use App\Models\Level; use App\Models\Modification; use App\Models\Platform; use App\Models\Status; +use App\Models\System; use App\Services\SubmissionsService; use App\Services\XenforoApiService; use Illuminate\Http\Request; @@ -39,19 +41,27 @@ class SubmissionController extends Controller 'words' => FormHelpers::getEntryFormWords($section), 'isEdit' => false, 'oldModifications' => old( 'modifications', [] ), + 'oldSystems' => old( 'systems', [] ), 'oldLanguages' => old( 'languages', [] ), + 'oldCategories' => old( 'categories', [] ), 'oldFilesArray' => $this->services->prepareOldFiles( null ) ]; if( $data['words'] === [] ) abort(500); - if( section_must_be( 'romhacks', $section ) ){ + if( section_must_be( ['romhacks', 'lua-scripts'], $section ) ){ $data['modifications'] = Modification::orderBy('name')->get(); } - if( section_must_be( [ 'romhacks', 'translations', 'homebrew' ], $section ) ){ + if( section_must_be( [ 'romhacks', 'translations', 'homebrew', 'lua-scripts' ], $section ) ){ $data['statuses'] = Status::orderBy('id')->get(); } + if( section_must_be( 'utilities' , $section ) ){ + $data['systems'] = System::orderBy('name')->get(); + } + if( section_must_be( [ 'utilities', 'documents' ], $section ) ) { + $data['levels'] = Level::orderBy('id')->get(); + } return view('submissions.create', $data); } @@ -68,19 +78,27 @@ class SubmissionController extends Controller 'words' => FormHelpers::getEntryFormWords($section), 'isEdit' => true, 'oldModifications' => old('modifications', $entry->modifications->pluck('id')->toArray() ?? [] ), + 'oldSystems' => old( 'systems', $entry->systems->pluck('id')->toArray() ?? [] ), 'oldLanguages' => old('languages', $entry->languages->pluck('id')->toArray() ?? [] ), + 'oldCategories' => old('categories', $entry->categories->pluck('id')->toArray() ?? [] ), 'oldFilesArray' => $this->services->prepareOldFiles( $entry ) ]; if( $data['words'] === [] ) abort(500); - if( section_must_be( 'romhacks', $section ) ){ + if( section_must_be( [ 'romhacks', 'lua-scripts' ], $section ) ){ $data['modifications'] = Modification::orderBy('name')->get(); } - if( section_must_be( [ 'romhacks', 'translations' ], $section ) ){ + if( section_must_be( [ 'romhacks', 'translations', 'homebrew', 'lua-scripts' ], $section ) ){ $data['statuses'] = Status::orderBy('id')->get(); } + if( section_must_be( 'utilities' , $section ) ){ + $data['systems'] = System::orderBy('name')->get(); + } + if( section_must_be( [ 'utilities', 'documents' ], $section ) ) { + $data['levels'] = Level::orderBy('id')->get(); + } return view('submissions.edit', $data); } diff --git a/app/Http/Requests/StoreEntryRequest.php b/app/Http/Requests/StoreEntryRequest.php index 10b73ef..5884b85 100644 --- a/app/Http/Requests/StoreEntryRequest.php +++ b/app/Http/Requests/StoreEntryRequest.php @@ -70,28 +70,49 @@ class StoreEntryRequest extends FormRequest $rules['entry_title'] = "nullable|string|max:255"; } - if( section_must_be( 'romhacks', $section ) ){ + if( section_must_be( ['romhacks', 'lua-scripts'], $section ) ){ $rules['modifications'] = 'array|required|min:1'; $rules['modifications.*'] = 'integer|exists:modifications,id'; + } else if( section_must_be( 'utilities', $section ) ){ + $rules['categories'] = 'array|required|min:1'; + $rules['categories.*'] = 'integer|exists:categories,id'; + } + + if( section_must_be( 'utilities', $section ) ){ + $rules['systems'] = 'array|required|min:1'; + $rules['systems.*'] = 'integer|exists:systems,id'; } $rules['version'] = 'required|string|max:50'; $rules['release-date'] = 'required|date'; - $rules['status'] = 'required|integer|exists:statuses,id'; + if( section_must_not_be( 'utilities', $section ) ){ + $rules['status'] = 'required|integer|exists:statuses,id'; + } else { + $rules['level'] = 'required|integer|exists:levels,id'; + } $rules['description'] = 'required|string'; - if( section_must_be( ['romhacks', 'translations' ], $section ) ){ + $rules['game_selection_mode'] = 'required|string|in:game,platform,none'; + $gameSelectionMode = $this->input('game_selection_mode') !== '' ? $this->input('game_selection_mode') : 'game'; + + if( $gameSelectionMode === 'none' ){ + // ... + } else if( $gameSelectionMode === 'platform' ){ + $rules['platform_only_id'] = 'required|integer|exists:platforms,id'; + } else { $rules['game_id'] = 'required_without:new-game-title|nullable|integer|exists:games,id'; $rules['new-game-title'] = 'required_without:game_id|nullable|string|max:255'; $rules['new-game-platform'] = 'required_with:new-game-title|nullable|integer|exists:platforms,id'; $rules['new-game-genre'] = 'required_with:new-game-title|integer|nullable|exists:genres,id'; } - $rules['hashes'] = 'array|required|min:1'; - $rules['hashes.*.filename'] = 'required|string|max:512'; - $rules['hashes.*.hash_crc32'] = 'required|string|max:512'; - $rules['hashes.*.hash_sha1'] = 'required|string|max:512'; - $rules['hashes.*.verified'] = 'required|string|max:512'; + if( section_must_be( ['translations', 'romhacks'], $section ) ){ + $rules['hashes'] = 'array|required|min:1'; + $rules['hashes.*.filename'] = 'required|string|max:512'; + $rules['hashes.*.hash_crc32'] = 'required|string|max:512'; + $rules['hashes.*.hash_sha1'] = 'required|string|max:512'; + $rules['hashes.*.verified'] = 'required|string|max:512'; + } $rules['languages'] = 'array|required|min:1'; $rules['languages.*'] = 'integer|exists:languages,id'; diff --git a/app/Livewire/Database.php b/app/Livewire/Database.php index fa9d1c9..da4a926 100644 --- a/app/Livewire/Database.php +++ b/app/Livewire/Database.php @@ -3,13 +3,16 @@ namespace App\Livewire; use App\Models\Author; +use App\Models\Category; use App\Models\Entry; use App\Models\Game; use App\Models\Genre; use App\Models\Language; +use App\Models\Level; use App\Models\Modification; use App\Models\Platform; use App\Models\Status; +use App\Models\System; use Livewire\Attributes\Url; use Livewire\Component; use Livewire\WithPagination; @@ -114,6 +117,41 @@ class Database extends Component #[Url(except:'or')] public string $modificationsMode = 'or'; + /** + * Categories IDs filter. + * @var array + */ + #[Url(except:[])] + public array $categories = []; + + /** + * Categories mode and/or + * @var string + */ + #[Url(except:['or'])] + public string $categoriesMode = 'or'; + + /** + * Systems IDs filter. + * @var array + */ + #[Url(except:[])] + public array $systems = []; + + /** + * Systems mode and/or + * @var string + */ + #[Url(except:['or'])] + public string $systemsMode = 'or'; + + /** + * Levels IDs filter. + * @var array + */ + #[Url(except:[])] + public array $levels = []; + /** * Sort by field. * @var string @@ -164,11 +202,16 @@ class Database extends Component public function updatedLanguagesMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); } public function updatedModifications(): void { $this->resetPage(); $this->dispatch('filters-updated'); } public function updatedModificationsMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); } + public function updatedCategories(): void { $this->resetPage(); $this->dispatch('filters-updated'); } + public function updatedCategoriesMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); } + public function updatedSystems(): void { $this->resetPage(); $this->dispatch('filters-updated'); } + public function updatedSystemsMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); } + public function updatedLevels(): void { $this->resetPage(); $this->dispatch('filters-updated'); } public function clearFilters(): void { $this->reset([ - 'search', 'types', 'platforms', 'genres', 'statuses', 'authors', 'authorsMode', 'languages', 'languagesMode', 'modifications', 'modificationsMode' + 'search', 'types', 'platforms', 'genres', 'statuses', 'authors', 'authorsMode', 'languages', 'languagesMode', 'modifications', 'modificationsMode', 'categories', 'categoriesMode', 'systems', 'systemsMode', 'levels' ]); $this->resetPage(); } @@ -188,7 +231,7 @@ class Database extends Component private function buildQuery() { $query = Entry::query()->published()->with([ - 'game.platform', 'game.genre', 'status', 'authors', 'languages' + 'game.platform', 'game.genre', 'status', 'authors', 'languages', 'level', 'systems', 'categories', 'modifications' ]); if( $this->search ) { @@ -253,6 +296,30 @@ class Database extends Component } } + if( $this->levels ) { + $query->whereIn('level_id', $this->levels); + } + + if( $this->categories ) { + if( $this->categoriesMode === 'and' ) { + foreach ( $this->categories as $categoryId ) { + $query->whereHas('categories', fn($q) => $q->where('categories.id', $categoryId)); + } + } else { + $query->whereHas('categories', fn($q) => $q->whereIn('categories.id', $this->categories)); + } + } + + if( $this->systems ) { + if( $this->systemsMode === 'and' ) { + foreach ( $this->systems as $systemId ) { + $query->whereHas('systems', fn($q) => $q->where('systems.id', $systemId)); + } + } else { + $query->whereHas('systems', fn($q) => $q->whereIn('systems.id', $this->systems)); + } + } + return $query->orderBy($this->sortBy, $this->sortDir); } @@ -267,6 +334,9 @@ class Database extends Component 'allAuthors' => Author::orderBy('name')->get(), 'allLanguages' => Language::orderBy('name')->get(), 'allModifications' => Modification::orderBy('name')->get(), + 'allCategories' => Category::orderBy('name')->get(), + 'allLevels' => Level::orderBy('name')->get(), + 'allSystems' => System::orderBy('name')->get(), ]); } } diff --git a/app/Livewire/GameSelector.php b/app/Livewire/GameSelector.php index 84b0b5b..2446d77 100644 --- a/app/Livewire/GameSelector.php +++ b/app/Livewire/GameSelector.php @@ -16,6 +16,17 @@ class GameSelector extends Component public const int REQUIRED_CHARS = 3; + /** + * Which section we can change selection mode. + */ + public const array CHANGE_SECTION_MODE = [ 'utilities', 'documents' ]; + + /** + * Selection mode between game|platform|none. + * @var string + */ + public string $selectionMode = 'game'; + /** * If we are in new game mode. * @var bool @@ -70,9 +81,25 @@ class GameSelector extends Component */ public bool $dropdown = false; - public function mount( ?int $gameId = null, ?string $newGameTitle = null, ?int $newGamePlatform = null, ?int $newGameGenre = null ): void + /** + * In platform mode. + * @var int|null + */ + public ?int $platformModeId = null; + + /** + * In platform mode. + * @var string|null + */ + public ?string $platformModeName = null; + + public ?string $section = null; + + public function mount( ?int $gameId = null, ?string $newGameTitle = null, ?int $newGamePlatform = null, ?int $newGameGenre = null, ?string $section, ?int $platformOnlyId ): void { + $this->section = $section; + // If we selected an existent game. if( $gameId ){ $game = Game::with(['platform','genre'])->find($gameId); @@ -93,6 +120,36 @@ class GameSelector extends Component $this->gamePlatformId = is_numeric($newGamePlatform) ? (int) $newGamePlatform : null; $this->gameGenreId = is_numeric($newGameGenre) ? (int) $newGameGenre : null; } + + if( in_array( $section, self::CHANGE_SECTION_MODE ) ) { + if ($platformOnlyId) { + $this->selectionMode = 'platform'; + $this->platformModeId = $platformOnlyId; + $platform = Platform::find($platformOnlyId); + if ($platform) { + $this->platformModeName = $platform->name; + } else { + $this->platformModeId = null; + } + } + } + } + + public function setSelectionMode(string $mode): void + { + if( !in_array( $this->section, self::CHANGE_SECTION_MODE ) ) + return; + + $this->selectionMode = $mode; + + if( $mode !== 'game' ){ + $this->clearGame(); + $this->newGame = false; + } + if( $mode !== 'platform' ){ + $this->platformModeId = null; + $this->platformModeName = null; + } } /** @@ -134,6 +191,15 @@ class GameSelector extends Component } + public function selectPlatformOnly( int $id ): void + { + $platform = Platform::find($id); + if( $platform ){ + $this->platformModeId = $platform->id; + $this->platformModeName = $platform->name; + } + } + /** * Clear existent game selection. * @return void @@ -179,6 +245,10 @@ class GameSelector extends Component } $data['hasOldNewGame'] = old('new-game-title') || old('new-game-platform') || old('new-game-genre'); + $data['canChangeSelection'] = in_array( $this->section, self::CHANGE_SECTION_MODE ); + if( in_array( $this->section, self::CHANGE_SECTION_MODE ) ) + $data['platforms'] = Platform::orderBy('name')->get(); + return view('livewire.game-selector', $data ); } } diff --git a/app/Models/Author.php b/app/Models/Author.php index 357367d..56eacce 100644 --- a/app/Models/Author.php +++ b/app/Models/Author.php @@ -8,6 +8,28 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; +/** + * @property int $id + * @property string $name + * @property string $slug + * @property string|null $website + * @property int|null $user_id + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Database\Eloquent\Collection $entries + * @property-read int|null $entries_count + * @method static \Illuminate\Database\Eloquent\Builder|Author newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Author newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Author query() + * @method static \Illuminate\Database\Eloquent\Builder|Author whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Author whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Author whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|Author whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|Author whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Author whereUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Author whereWebsite($value) + * @mixin \Eloquent + */ class Author extends Model { protected $fillable = [ diff --git a/app/Models/Category.php b/app/Models/Category.php new file mode 100644 index 0000000..69e3e41 --- /dev/null +++ b/app/Models/Category.php @@ -0,0 +1,33 @@ +|null $restricted_to + * @method static \Illuminate\Database\Eloquent\Builder|Category newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Category newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Category query() + * @method static \Illuminate\Database\Eloquent\Builder|Category whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Category whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Category whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|Category whereRestrictedTo($value) + * @method static \Illuminate\Database\Eloquent\Builder|Category whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|Category whereUpdatedAt($value) + * @mixin \Eloquent + */ +class Category extends Model +{ + protected $fillable = ['name', 'slug', 'restricted_to']; + + protected $casts = [ + 'restricted_to' => 'array', + ]; + +} diff --git a/app/Models/Entry.php b/app/Models/Entry.php index db044fb..ef532b9 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -2,17 +2,100 @@ namespace App\Models; +use App\Traits\HasGallery; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; +use Monolog\Level; +/** + * @property int $id + * @property string $type + * @property string|null $title + * @property string|null $slug + * @property string|null $description + * @property string|null $main_image + * @property string $state + * @property string|null $staff_comment + * @property \Illuminate\Support\Carbon|null $rejected_at + * @property bool $featured + * @property int|null $game_id + * @property int|null $platform_id + * @property int|null $status_id + * @property string|null $version + * @property \Illuminate\Support\Carbon|null $release_date + * @property string|null $staff_credits + * @property string|null $relevant_link + * @property string|null $youtube_link + * @property int $user_id + * @property int|null $comments_thread_id + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property string|null $complete_title + * @property \Illuminate\Support\Carbon|null $deleted_at + * @property int|null $level_id + * @property-read \Illuminate\Database\Eloquent\Collection $authors + * @property-read int|null $authors_count + * @property-read \Illuminate\Database\Eloquent\Collection $categories + * @property-read int|null $categories_count + * @property-read \Illuminate\Database\Eloquent\Collection $files + * @property-read int|null $files_count + * @property-read \Illuminate\Database\Eloquent\Collection $gallery + * @property-read int|null $gallery_count + * @property-read \App\Models\Game|null $game + * @property-read \Illuminate\Database\Eloquent\Collection $hashes + * @property-read int|null $hashes_count + * @property-read \Illuminate\Database\Eloquent\Collection $languages + * @property-read int|null $languages_count + * @property-read \App\Models\Level|null $level + * @property-read \Illuminate\Database\Eloquent\Collection $modifications + * @property-read int|null $modifications_count + * @property-read \App\Models\Platform|null $platform + * @property-read \App\Models\Status|null $status + * @property-read \Illuminate\Database\Eloquent\Collection $systems + * @property-read int|null $systems_count + * @method static Builder|Entry inQueue(int $daysRejected = 7) + * @method static Builder|Entry newModelQuery() + * @method static Builder|Entry newQuery() + * @method static Builder|Entry onlyTrashed() + * @method static Builder|Entry published() + * @method static Builder|Entry query() + * @method static Builder|Entry whereCommentsThreadId($value) + * @method static Builder|Entry whereCompleteTitle($value) + * @method static Builder|Entry whereCreatedAt($value) + * @method static Builder|Entry whereDeletedAt($value) + * @method static Builder|Entry whereDescription($value) + * @method static Builder|Entry whereFeatured($value) + * @method static Builder|Entry whereGameId($value) + * @method static Builder|Entry whereId($value) + * @method static Builder|Entry whereLevelId($value) + * @method static Builder|Entry whereMainImage($value) + * @method static Builder|Entry wherePlatformId($value) + * @method static Builder|Entry whereRejectedAt($value) + * @method static Builder|Entry whereReleaseDate($value) + * @method static Builder|Entry whereRelevantLink($value) + * @method static Builder|Entry whereSlug($value) + * @method static Builder|Entry whereStaffComment($value) + * @method static Builder|Entry whereStaffCredits($value) + * @method static Builder|Entry whereState($value) + * @method static Builder|Entry whereStatusId($value) + * @method static Builder|Entry whereTitle($value) + * @method static Builder|Entry whereType($value) + * @method static Builder|Entry whereUpdatedAt($value) + * @method static Builder|Entry whereUserId($value) + * @method static Builder|Entry whereVersion($value) + * @method static Builder|Entry whereYoutubeLink($value) + * @method static Builder|Entry withTrashed(bool $withTrashed = true) + * @method static Builder|Entry withoutTrashed() + * @mixin \Eloquent + */ class Entry extends Model { - use SoftDeletes; + use SoftDeletes, HasGallery; /** * @var string[] @@ -38,6 +121,7 @@ class Entry extends Model 'comments_thread_id', 'staff_comment', 'rejected_at', + 'level_id' ]; /** @@ -81,6 +165,10 @@ class Entry extends Model return $this->belongsTo(Status::class ); } + public function level(): BelongsTo { + return $this->belongsTo(\App\Models\Level::class); + } + public function authors(): BelongsToMany { return $this->belongsToMany(Author::class, 'entry_authors'); } @@ -93,12 +181,16 @@ class Entry extends Model return $this->belongsToMany( Modification::class, 'entry_modifications'); } - public function files(): HasMany { - return $this->hasMany(EntryFile::class)->orderBy('filename'); + public function categories(): BelongsToMany { + return $this->belongsToMany(Category::class, 'entry_categories'); } - public function gallery(): HasMany { - return $this->hasMany(EntryGallery::class)->orderBy('id'); + public function systems(): BelongsToMany { + return $this->belongsToMany(System::class, 'entry_systems'); + } + + public function files(): HasMany { + return $this->hasMany(EntryFile::class)->orderBy('filename'); } public function hashes(): HasMany { diff --git a/app/Models/EntryFile.php b/app/Models/EntryFile.php index c738572..99bcd7f 100644 --- a/app/Models/EntryFile.php +++ b/app/Models/EntryFile.php @@ -5,6 +5,39 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +/** + * @property int $id + * @property int $entry_id + * @property string $filename + * @property string $filepath + * @property string $favorite_server + * @property int $favorite_at + * @property int|null $filesize + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property string $file_uuid + * @property string $state + * @property int $online_patcher + * @property int $secondary_online_patcher + * @property-read \App\Models\Entry|null $entry + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile query() + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile whereEntryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile whereFavoriteAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile whereFavoriteServer($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile whereFileUuid($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile whereFilename($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile whereFilepath($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile whereFilesize($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile whereOnlinePatcher($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile whereSecondaryOnlinePatcher($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile whereState($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryFile whereUpdatedAt($value) + * @mixin \Eloquent + */ class EntryFile extends Model { protected $fillable = [ diff --git a/app/Models/EntryGallery.php b/app/Models/EntryGallery.php index 0b478ec..48b6ff2 100644 --- a/app/Models/EntryGallery.php +++ b/app/Models/EntryGallery.php @@ -2,10 +2,7 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Model; - -class EntryGallery extends Model -{ - protected $fillable = ['entry_id','image']; - -} +/** + * @deprecated Use Gallery instead. + */ +class EntryGallery extends Gallery {} diff --git a/app/Models/EntryHash.php b/app/Models/EntryHash.php index 35ef829..bdeb050 100644 --- a/app/Models/EntryHash.php +++ b/app/Models/EntryHash.php @@ -5,6 +5,29 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +/** + * @property int $id + * @property int $entry_id + * @property string $filename + * @property string $hash_crc32 + * @property string $hash_sha1 + * @property string $verified + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \App\Models\Entry|null $entry + * @method static \Illuminate\Database\Eloquent\Builder|EntryHash newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|EntryHash newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|EntryHash query() + * @method static \Illuminate\Database\Eloquent\Builder|EntryHash whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryHash whereEntryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryHash whereFilename($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryHash whereHashCrc32($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryHash whereHashSha1($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryHash whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryHash whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|EntryHash whereVerified($value) + * @mixin \Eloquent + */ class EntryHash extends Model { protected $fillable = [ diff --git a/app/Models/Gallery.php b/app/Models/Gallery.php new file mode 100644 index 0000000..3d5f7dd --- /dev/null +++ b/app/Models/Gallery.php @@ -0,0 +1,34 @@ +|Gallery newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Gallery newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Gallery query() + * @method static \Illuminate\Database\Eloquent\Builder|Gallery whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Gallery whereEntryId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Gallery whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Gallery whereImage($value) + * @method static \Illuminate\Database\Eloquent\Builder|Gallery whereUpdatedAt($value) + * @mixin \Eloquent + */ +class Gallery extends Model +{ + protected $table = 'galleries'; + protected $fillable = ['image', 'galleryable_id', 'galleryable_type']; + + public function galleryable(): MorphTo + { + return $this->morphTo(); + } + +} diff --git a/app/Models/Game.php b/app/Models/Game.php index db4b115..0878b20 100644 --- a/app/Models/Game.php +++ b/app/Models/Game.php @@ -5,6 +5,30 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +/** + * @property int $id + * @property string $name + * @property string $slug + * @property int $platform_id + * @property int $genre_id + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Database\Eloquent\Collection $entries + * @property-read int|null $entries_count + * @property-read \App\Models\Genre $genre + * @property-read \App\Models\Platform $platform + * @method static \Illuminate\Database\Eloquent\Builder|Game newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Game newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Game query() + * @method static \Illuminate\Database\Eloquent\Builder|Game whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Game whereGenreId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Game whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Game whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|Game wherePlatformId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Game whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|Game whereUpdatedAt($value) + * @mixin \Eloquent + */ class Game extends Model { /** diff --git a/app/Models/Genre.php b/app/Models/Genre.php index c2d3551..5088894 100644 --- a/app/Models/Genre.php +++ b/app/Models/Genre.php @@ -6,6 +6,24 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; +/** + * @property int $id + * @property string $name + * @property string $slug + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Database\Eloquent\Collection $games + * @property-read int|null $games_count + * @method static \Illuminate\Database\Eloquent\Builder|Genre newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Genre newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Genre query() + * @method static \Illuminate\Database\Eloquent\Builder|Genre whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Genre whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Genre whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|Genre whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|Genre whereUpdatedAt($value) + * @mixin \Eloquent + */ class Genre extends Model { protected $fillable = [ 'name', 'slug' ]; diff --git a/app/Models/Language.php b/app/Models/Language.php index a2de75b..ba1cca0 100644 --- a/app/Models/Language.php +++ b/app/Models/Language.php @@ -6,6 +6,24 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; +/** + * @property int $id + * @property string $name + * @property string $slug + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Database\Eloquent\Collection $entries + * @property-read int|null $entries_count + * @method static \Illuminate\Database\Eloquent\Builder|Language newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Language newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Language query() + * @method static \Illuminate\Database\Eloquent\Builder|Language whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Language whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Language whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|Language whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|Language whereUpdatedAt($value) + * @mixin \Eloquent + */ class Language extends Model { protected $fillable = [ 'name', 'slug' ]; diff --git a/app/Models/Level.php b/app/Models/Level.php new file mode 100644 index 0000000..d205d45 --- /dev/null +++ b/app/Models/Level.php @@ -0,0 +1,26 @@ +|Level newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Level newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Level query() + * @method static \Illuminate\Database\Eloquent\Builder|Level whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Level whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Level whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|Level whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|Level whereUpdatedAt($value) + * @mixin \Eloquent + */ +class Level extends Model +{ + protected $fillable = ['name', 'slug']; +} diff --git a/app/Models/Modification.php b/app/Models/Modification.php index fd7054f..d2596d1 100644 --- a/app/Models/Modification.php +++ b/app/Models/Modification.php @@ -4,6 +4,22 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; +/** + * @property int $id + * @property string $name + * @property string $slug + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @method static \Illuminate\Database\Eloquent\Builder|Modification newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Modification newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Modification query() + * @method static \Illuminate\Database\Eloquent\Builder|Modification whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Modification whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Modification whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|Modification whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|Modification whereUpdatedAt($value) + * @mixin \Eloquent + */ class Modification extends Model { protected $fillable = [ 'name', 'slug' ]; diff --git a/app/Models/Platform.php b/app/Models/Platform.php index cac5bf3..8fc5269 100644 --- a/app/Models/Platform.php +++ b/app/Models/Platform.php @@ -5,6 +5,28 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; +/** + * @property int $id + * @property string $name + * @property string $slug + * @property string|null $short_name + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Database\Eloquent\Collection $entries + * @property-read int|null $entries_count + * @property-read \Illuminate\Database\Eloquent\Collection $games + * @property-read int|null $games_count + * @method static \Illuminate\Database\Eloquent\Builder|Platform newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Platform newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Platform query() + * @method static \Illuminate\Database\Eloquent\Builder|Platform whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Platform whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Platform whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|Platform whereShortName($value) + * @method static \Illuminate\Database\Eloquent\Builder|Platform whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|Platform whereUpdatedAt($value) + * @mixin \Eloquent + */ class Platform extends Model { diff --git a/app/Models/Status.php b/app/Models/Status.php index b6276a9..8dc7094 100644 --- a/app/Models/Status.php +++ b/app/Models/Status.php @@ -4,6 +4,22 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; +/** + * @property int $id + * @property string $name + * @property string $slug + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @method static \Illuminate\Database\Eloquent\Builder|Status newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Status newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Status query() + * @method static \Illuminate\Database\Eloquent\Builder|Status whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Status whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|Status whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|Status whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|Status whereUpdatedAt($value) + * @mixin \Eloquent + */ class Status extends Model { protected $fillable = ['name', 'slug']; diff --git a/app/Models/System.php b/app/Models/System.php new file mode 100644 index 0000000..65d86e3 --- /dev/null +++ b/app/Models/System.php @@ -0,0 +1,26 @@ +|System newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|System newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|System query() + * @method static \Illuminate\Database\Eloquent\Builder|System whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|System whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|System whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|System whereSlug($value) + * @method static \Illuminate\Database\Eloquent\Builder|System whereUpdatedAt($value) + * @mixin \Eloquent + */ +class System extends Model +{ + protected $fillable = ['name', 'slug']; +} diff --git a/app/Models/User.php b/app/Models/User.php index f6ba1d2..e46ea30 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -10,6 +10,31 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +/** + * @property int $id + * @property string $name + * @property string $email + * @property \Illuminate\Support\Carbon|null $email_verified_at + * @property string $password + * @property string|null $remember_token + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read int|null $notifications_count + * @method static \Database\Factories\UserFactory factory($count = null, $state = []) + * @method static \Illuminate\Database\Eloquent\Builder|User newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|User newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|User query() + * @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value) + * @method static \Illuminate\Database\Eloquent\Builder|User whereEmailVerifiedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|User whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|User whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|User wherePassword($value) + * @method static \Illuminate\Database\Eloquent\Builder|User whereRememberToken($value) + * @method static \Illuminate\Database\Eloquent\Builder|User whereUpdatedAt($value) + * @mixin \Eloquent + */ #[Fillable(['name', 'email', 'password'])] #[Hidden(['password', 'remember_token'])] class User extends Authenticatable diff --git a/app/Services/SubmissionsService.php b/app/Services/SubmissionsService.php index c397316..b7df22b 100644 --- a/app/Services/SubmissionsService.php +++ b/app/Services/SubmissionsService.php @@ -8,15 +8,17 @@ use App\Helpers\XenForoHelpers; use App\Http\Requests\StoreEntryRequest; use App\Jobs\CreateXenForoCommentsThread; use App\Models\Author; +use App\Models\Category; use App\Models\Entry; use App\Models\EntryFile; -use App\Models\EntryGallery; +use App\Models\Gallery; use App\Models\EntryHash; use App\Models\Game; use App\Models\Genre; use App\Models\Language; use App\Models\Modification; use App\Models\Platform; +use App\Models\System; use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\DB; @@ -118,11 +120,7 @@ class SubmissionsService { $entry = DB::transaction(function () use ( $user_id ) { // STEP 2 : Create game. - - $gameId = null; - if( section_must_be( ['romhacks', 'translations'], $this->section ) ){ - $gameId = $this->Step2_CreateAndReturnGameId(); - } + $gameId = $this->Step2_CreateAndReturnGameId(); // STEP 3 : Create Complete title. $completeTitle = $this->Step3_BuildCompleteTitle( $gameId ); @@ -132,7 +130,7 @@ class SubmissionsService { if( section_must_be( 'translations', $this->section ) && !$this->request->input('entry_title') ){ - $entryTitle = Game::find($gameId)->name; + $entryTitle = Game::find($gameId)->name ?? ""; } else { $entryTitle = $this->request->input('entry_title'); } @@ -149,6 +147,7 @@ class SubmissionsService { 'main_image' => $this->request->input('main-image'), 'state' => $this->request->input('submit-state'), 'game_id' => $gameId, + 'platform_id' => $this->request->input('platform_only_id'), 'status_id' => $this->request->input('status'), 'version' => $this->request->input('version'), 'release_date' => $this->request->input('release-date'), @@ -157,6 +156,7 @@ class SubmissionsService { 'youtube_link' => $this->request->input('youtube_video'), 'user_id' => $user_id, 'complete_title' => $completeTitle, + 'level_id' => $this->request->input('level') ]; $entry = Entry::create( $fields ); @@ -165,19 +165,28 @@ class SubmissionsService { $this->Step7_SaveEntryFiles( $entry ); // STEP 8 : Save hashes. - $this->Step8_SaveHashes( $entry->id ); + if( section_must_be( ['translations', 'romhacks' ], $this->section ) ) + $this->Step8_SaveHashes( $entry->id ); // STEP 9 : Save Authors. $this->Step9_SaveAuthors( $entry ); // STEP 10 : Save Modifications. - if( section_must_be( 'romhacks', $this->section ) ){ + if( section_must_be( ['romhacks','lua-scripts'], $this->section ) ){ $this->Step10_SaveRomhacksModifications( $entry ); } + if( section_must_be( 'utilities', $this->section ) ){ + $this->Step10_SaveUtilitiesSystems( $entry ); + } // STEP 11 : Save Languages $this->Step11_SaveLanguages( $entry ); + // STEP 11.5 : Save Categories + if( section_must_be( 'utilities', $this->section ) ) { + $this->Step11_5_SaveCategories($entry); + } + // STEP 12 : Prepare Gallery images. $this->Step12a_PrepareGalleryImages( $entry ); @@ -207,6 +216,10 @@ class SubmissionsService { */ private function Step2_CreateAndReturnGameId(): ?int { + $mode = $this->request->input('game_selection_mode', 'game'); + if( $mode !== 'game' ) + return null; + // Already existing game. if( $this->request->input('game_id') ) return $this->request->input('game_id'); @@ -261,9 +274,9 @@ class SubmissionsService { if( section_must_be( 'translations', $this->section ) ) { $fields['languages_string'] = Language::whereIn('id', $this->request->input('languages', []))->pluck('name')->implode(', '); } - if( section_must_be(['romhacks', 'translations', 'homebrew', 'lua-scripts', 'tutorials'], $this->section ) ) { + if( section_must_be(['romhacks', 'translations', 'homebrew', 'lua-scripts'], $this->section ) ) { // TODO: Add single platform ID compatibility. - $fields['platform_name'] = $gameId ? Game::find( $gameId )->platform->name : null; + $fields['platform_name'] = $gameId ? Game::find( $gameId )->platform->name : Platform::find( $this->request->input('platform_only_id') )?->name ?? null; } return EntryHelpers::buildCompleteTitle( $this->section, $fields ); @@ -381,6 +394,24 @@ class SubmissionsService { } } + /** + * @param Entry $entry + * + * @return void + * @throws SubmissionException + */ + private function Step10_SaveUtilitiesSystems( Entry $entry ): void + { + // TODO: Replace by edit version + + foreach ( $this->request->input('systems', [] ) ?? [] as $systemId ) { + $system = System::find( $systemId ); + if( !$system ) + throw new SubmissionException( "System {$systemId} does not exist." ); + $entry->systems()->attach( $system->id ); + } + } + /** * @param Entry $entry * @@ -400,11 +431,29 @@ class SubmissionsService { } + /** + * @param Entry $entry + * + * @return void + * @throws SubmissionException + */ + private function Step11_5_SaveCategories( Entry $entry ): void + { + // TODO: Replace by edit version. + + foreach ( $this->request->input('categories', [] ) ?? [] as $categoryId ) { + $category = Category::find( $categoryId ); + if( !$category ) + throw new SubmissionException( "Category {$categoryId} does not exist." ); + $entry->categories()->attach( $category->id ); + } + + } + private function Step12a_PrepareGalleryImages( Entry $entry ): void { foreach ( $this->request->input('gallery', [] ) ?? [] as $imagePath ) { - EntryGallery::create([ - 'entry_id' => $entry->id, + $entry->gallery()->create([ 'image' => $imagePath, ]); } @@ -464,9 +513,7 @@ class SubmissionsService { // STEP 2: Create game if different. $gameId = null; - if( section_must_be( ['romhacks', 'translations' ], $this->section ) ){ - $gameId = $this->eStep2_VerifyCreateAndEditGameId(); - } + $gameId = $this->eStep2_VerifyCreateAndEditGameId(); // STEP 3: Recreate complete title and refresh slug if needed. $completeTitle = $this->Step3_BuildCompleteTitle( $gameId ); @@ -494,6 +541,7 @@ class SubmissionsService { 'main_image' => $this->request->input('main-image'), 'state' => $this->request->input('submit-state'), 'game_id' => $gameId, + 'platform_id' => $this->request->input('platform_only_id'), 'status_id' => $this->request->input('status'), 'version' => $this->request->input('version'), 'release_date' => $this->request->input('release-date'), @@ -502,12 +550,13 @@ class SubmissionsService { 'youtube_link' => $this->request->input('youtube_video'), 'user_id' => $user_id, 'complete_title' => $completeTitle, - 'comments_thread_id' => $this->request->input('comments_thread_id'), - 'featured' => $this->request->input('featured') ?? false, + 'level_id' => $this->request->input('level'), ]; if( \Auth::user()->can('moderate', $this->entry) ){ $fields['staff_comment'] = $this->request->input('staff_comment'); + $fields['featured'] = $this->request->input('featured') ?? false; + $fields['comments_thread_id'] = $this->request->input('comments_thread_id'); } $this->entry->update( $fields ); @@ -516,19 +565,27 @@ class SubmissionsService { $this->eStep6_UpdateEntryFiles( $this->entry->id ); // STEP 7: Update hashes. - $this->eStep7_UpdateHashes( $this->entry->id ); + if( section_must_be( ['translations', 'romhacks' ], $this->section ) ) + $this->eStep7_UpdateHashes( $this->entry->id ); // STEP 8: Update Authors. $this->eStep8_UpdateAuthors(); // STEP 9: Update romhacks modifications. - if( section_must_be( 'romhacks', $this->section ) ) { + if( section_must_be( ['romhacks', 'lua-scripts'], $this->section ) ) { $this->eStep9_UpdateRomhacksModifications(); } + if( section_must_be( 'utilities', $this->section ) ) { + $this->eStep9_UpdateUtilitiesSystems(); + } // STEP 10: Update Languages. $this->eStep10_UpdateLanguages(); + // STEP 10.5 : Update categories + if( section_must_be( 'utilities', $this->section ) ) + $this->eStep10_5_UpdateCategories(); + // STEP 11: Prepare new gallery images and prepare deletion of others ones. $galleryPaths = $this->eStep11a_UpdateGalleryImages(); @@ -558,8 +615,14 @@ class SubmissionsService { /** * @throws SubmissionException */ - private function eStep2_VerifyCreateAndEditGameId(): int + private function eStep2_VerifyCreateAndEditGameId(): ?int { + + $mode = $this->request->input('game_selection_mode', 'game'); + if ($mode !== 'game') { + return null; + } + // Already existing game. if( $this->request->input('game_id') ){ @@ -721,6 +784,27 @@ class SubmissionsService { } + /** + * @return void + * @throws SubmissionException + */ + private function eStep9_UpdateUtilitiesSystems(): void + { + $requestSystems = $this->request->input('systems', [] ) ?? []; + if( !empty( $requestSystems ) ){ + $valid = System::whereIn( 'id', $requestSystems )->pluck('id')->toArray(); + + if( count( $valid ) !== count( $requestSystems ) ){ + throw new SubmissionException( "One of the systems doesn't exist." ); + } + + + } + + $this->entry->systems()->sync( $requestSystems ); + + } + /** * @return void * @throws SubmissionException @@ -739,6 +823,24 @@ class SubmissionsService { $this->entry->languages()->sync( $requestLanguages ); } + /** + * @return void + * @throws SubmissionException + */ + private function eStep10_5_UpdateCategories(): void + { + $requestCategories = $this->request->input('categories', [] ) ?? []; + if( !empty( $requestCategories ) ){ + $valid = Category::whereIn( 'id', $requestCategories )->pluck('id')->toArray(); + if( count( $valid ) !== count( $requestCategories ) ){ + throw new SubmissionException( "One of the categories doesn't exist." ); + } + + } + + $this->entry->categories()->sync( $requestCategories ); + } + private function eStep11a_UpdateGalleryImages(): array { $requestGallery = $this->request->input('gallery', [] ) ?? []; @@ -747,14 +849,13 @@ class SubmissionsService { $needDeletion = array_diff( $existingGalleryPaths, $requestGallery ); if( !empty( $needDeletion ) ){ - EntryGallery::where('entry_id', $this->entry->id)->whereIn('image', $needDeletion )->delete(); + $this->entry->gallery()->whereIn('image', $needDeletion )->delete(); } $needAddition = array_diff( $requestGallery, $existingGalleryPaths ); $images = []; foreach( $needAddition as $imagePath ){ - $images[] = EntryGallery::create([ - 'entry_id' => $this->entry->id, + $images[] = $this->entry->gallery()->create([ 'image' => $imagePath, ]); } diff --git a/app/Traits/HasGallery.php b/app/Traits/HasGallery.php new file mode 100644 index 0000000..c775d50 --- /dev/null +++ b/app/Traits/HasGallery.php @@ -0,0 +1,14 @@ +morphMany(Gallery::class, 'galleryable')->orderBy('id'); + } +} diff --git a/app/View/Components/CategorySelector.php b/app/View/Components/CategorySelector.php new file mode 100644 index 0000000..44c7fdb --- /dev/null +++ b/app/View/Components/CategorySelector.php @@ -0,0 +1,41 @@ +categories = Category::query() + ->where(function ($query) { + $query->whereJsonContains('restricted_to', $this->section) + ->orWhereNull('restricted_to'); + }) + ->orderBy('name') + ->get(); + } + + /** + * Get the view / contents that represent the component. + */ + public function render(): View|Closure|string + { + return view('components.category-selector'); + } +} diff --git a/composer.json b/composer.json index dbd334c..cf73412 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,7 @@ }, "require-dev": { "barryvdh/laravel-debugbar": "^4.2", + "barryvdh/laravel-ide-helper": "^3.7", "fakerphp/faker": "^1.23", "larastan/larastan": "^3.9", "laravel/pail": "^1.2.5", diff --git a/composer.lock b/composer.lock index 953b57e..a713774 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a1c836758aaea45cc09c0c7786ea366f", + "content-hash": "79ce85f4c121f30d964f9322cb7120a4", "packages": [ { "name": "blade-ui-kit/blade-heroicons", @@ -8316,6 +8316,301 @@ ], "time": "2026-04-20T13:31:29+00:00" }, + { + "name": "barryvdh/laravel-ide-helper", + "version": "v3.7.0", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-ide-helper.git", + "reference": "ad7e37676f1ff985d55ef1b6b96a0c0a40f2609a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/ad7e37676f1ff985d55ef1b6b96a0c0a40f2609a", + "reference": "ad7e37676f1ff985d55ef1b6b96a0c0a40f2609a", + "shasum": "" + }, + "require": { + "barryvdh/reflection-docblock": "^2.4", + "composer/class-map-generator": "^1.0", + "ext-json": "*", + "illuminate/console": "^11.15 || ^12 || ^13.0", + "illuminate/database": "^11.15 || ^12 || ^13.0", + "illuminate/filesystem": "^11.15 || ^12 || ^13.0", + "illuminate/support": "^11.15 || ^12 || ^13.0", + "php": "^8.2" + }, + "require-dev": { + "ext-pdo_sqlite": "*", + "friendsofphp/php-cs-fixer": "^3", + "illuminate/config": "^11.15 || ^12 || ^13.0", + "illuminate/view": "^11.15 || ^12 || ^13.0", + "larastan/larastan": "^3.1", + "mockery/mockery": "^1.4", + "orchestra/testbench": "^9.2 || ^10 || ^11.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5 || ^11.5.3 || ^12.5.12", + "spatie/phpunit-snapshot-assertions": "^4 || ^5", + "vlucas/phpdotenv": "^5" + }, + "suggest": { + "illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9|^10|^11)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\LaravelIdeHelper\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.", + "keywords": [ + "autocomplete", + "codeintel", + "dev", + "helper", + "ide", + "laravel", + "netbeans", + "phpdoc", + "phpstorm", + "sublime" + ], + "support": { + "issues": "https://github.com/barryvdh/laravel-ide-helper/issues", + "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.7.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2026-03-17T14:12:51+00:00" + }, + { + "name": "barryvdh/reflection-docblock", + "version": "v2.4.1", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/ReflectionDocBlock.git", + "reference": "4f5ba70c30c81f2ce03a16a9965832cfcc31ed3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/4f5ba70c30c81f2ce03a16a9965832cfcc31ed3b", + "reference": "4f5ba70c30c81f2ce03a16a9965832cfcc31ed3b", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.14|^9" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Barryvdh": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "support": { + "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.4.1" + }, + "time": "2026-03-05T20:09:01+00:00" + }, + { + "name": "composer/class-map-generator", + "version": "1.7.3", + "source": { + "type": "git", + "url": "https://github.com/composer/class-map-generator.git", + "reference": "86d8208fc3c649a3a999daf1a63c25201be2990f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/86d8208fc3c649a3a999daf1a63c25201be2990f", + "reference": "86d8208fc3c649a3a999daf1a63c25201be2990f", + "shasum": "" + }, + "require": { + "composer/pcre": "^2.1 || ^3.1", + "php": "^7.2 || ^8.0", + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7 || ^8" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-deprecation-rules": "^1 || ^2", + "phpstan/phpstan-phpunit": "^1 || ^2", + "phpstan/phpstan-strict-rules": "^1.1 || ^2", + "phpunit/phpunit": "^8", + "symfony/filesystem": "^5.4 || ^6 || ^7 || ^8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\ClassMapGenerator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Utilities to scan PHP code and generate class maps.", + "keywords": [ + "classmap" + ], + "support": { + "issues": "https://github.com/composer/class-map-generator/issues", + "source": "https://github.com/composer/class-map-generator/tree/1.7.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2026-05-05T09:17:07+00:00" + }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, { "name": "fakerphp/faker", "version": "v1.24.1", @@ -10945,7 +11240,9 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.4" + "php": "^8.4", + "ext-xmlreader": "*", + "ext-simplexml": "*" }, "platform-dev": {}, "plugin-api-version": "2.9.0" diff --git a/database/migrations/2026_06_09_122425_create_category_table.php b/database/migrations/2026_06_09_122425_create_category_table.php new file mode 100644 index 0000000..4fe1bd3 --- /dev/null +++ b/database/migrations/2026_06_09_122425_create_category_table.php @@ -0,0 +1,29 @@ +id(); + $table->string('name'); + $table->string('slug')->unique(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('categories'); + } +}; diff --git a/database/migrations/2026_06_09_122655_create_os_table.php b/database/migrations/2026_06_09_122655_create_os_table.php new file mode 100644 index 0000000..7e119f4 --- /dev/null +++ b/database/migrations/2026_06_09_122655_create_os_table.php @@ -0,0 +1,29 @@ +id(); + $table->string('name'); + $table->string('slug')->unique(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('systems'); + } +}; diff --git a/database/migrations/2026_06_09_122817_create_level_table.php b/database/migrations/2026_06_09_122817_create_level_table.php new file mode 100644 index 0000000..13d321f --- /dev/null +++ b/database/migrations/2026_06_09_122817_create_level_table.php @@ -0,0 +1,29 @@ +id(); + $table->string('name'); + $table->string('slug')->unique(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('levels'); + } +}; diff --git a/database/migrations/2026_06_09_123242_add_entry_level_id_field.php b/database/migrations/2026_06_09_123242_add_entry_level_id_field.php new file mode 100644 index 0000000..a263bb9 --- /dev/null +++ b/database/migrations/2026_06_09_123242_add_entry_level_id_field.php @@ -0,0 +1,28 @@ +foreignId('level_id')->nullable()->constrained('levels')->nullOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('entries', function (Blueprint $table) { + $table->dropColumn('level_id'); + }); + } +}; diff --git a/database/migrations/2026_06_09_123458_create_entry_systems_table.php b/database/migrations/2026_06_09_123458_create_entry_systems_table.php new file mode 100644 index 0000000..a4e3fbf --- /dev/null +++ b/database/migrations/2026_06_09_123458_create_entry_systems_table.php @@ -0,0 +1,28 @@ +foreignId('entry_id')->constrained('entries')->cascadeOnDelete(); + $table->foreignId('system_id')->constrained('systems')->cascadeOnDelete(); + $table->primary(['entry_id', 'system_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('entry_systems'); + } +}; diff --git a/database/migrations/2026_06_09_123533_create_entry_categories_table.php b/database/migrations/2026_06_09_123533_create_entry_categories_table.php new file mode 100644 index 0000000..fce71b1 --- /dev/null +++ b/database/migrations/2026_06_09_123533_create_entry_categories_table.php @@ -0,0 +1,28 @@ +foreignId('entry_id')->constrained('entries')->cascadeOnDelete(); + $table->foreignId('category_id')->constrained('categories')->cascadeOnDelete(); + $table->primary(['entry_id', 'category_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('entry_categories'); + } +}; diff --git a/database/migrations/2026_06_09_124832_add_restricted_field_to_categories.php b/database/migrations/2026_06_09_124832_add_restricted_field_to_categories.php new file mode 100644 index 0000000..f63e4a6 --- /dev/null +++ b/database/migrations/2026_06_09_124832_add_restricted_field_to_categories.php @@ -0,0 +1,28 @@ +json('restricted_to')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('categories', function (Blueprint $table) { + $table->dropColumn('restricted_to'); + }); + } +}; diff --git a/database/migrations/2026_06_10_084936_make_entry_galleries_polymorphic.php b/database/migrations/2026_06_10_084936_make_entry_galleries_polymorphic.php new file mode 100644 index 0000000..391bf73 --- /dev/null +++ b/database/migrations/2026_06_10_084936_make_entry_galleries_polymorphic.php @@ -0,0 +1,40 @@ +string('galleryable_type')->default('App\\Models\\Entry') + ->after('id'); + + $table->renameColumn('entry_id', 'galleryable_id'); + + $table->index(['galleryable_type', 'galleryable_id']); + + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('galleries', function (Blueprint $table) { + $table->dropColumn('galleryable_type'); + $table->renameColumn('galleryable_id', 'entry_id'); + $table->dropIndex(['galleryable_type', 'galleryable_id']); + }); + + Schema::rename('galleries', 'entry_galleries'); + } +}; diff --git a/database/migrations/2026_06_10_090320_create_news_table.php b/database/migrations/2026_06_10_090320_create_news_table.php new file mode 100644 index 0000000..dab2a32 --- /dev/null +++ b/database/migrations/2026_06_10_090320_create_news_table.php @@ -0,0 +1,27 @@ +id(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('news'); + } +}; diff --git a/extra.less b/extra.less index ea5932a..62e665d 100644 --- a/extra.less +++ b/extra.less @@ -63,7 +63,7 @@ ul { --error: #e57373; --info: #1976d2; --success: #81c784; - --success2: #388e3c; + --success2: #fdeb0f; /* Typo */ --typography: 'Segoe UI', 'San Francisco', 'Helvetica Neue', sans-serif; diff --git a/resources/css/components/common.css b/resources/css/components/common.css index c74aa62..e4e392e 100644 --- a/resources/css/components/common.css +++ b/resources/css/components/common.css @@ -104,6 +104,11 @@ color: var(--text); border-color: var(--success2); } +.badge.yellow, .badge.utilities { + background-color: #fdeb0f; + color: #000; + border-color: #fdeb0f; +} .topbar-badge { position: absolute; diff --git a/resources/css/components/forms.css b/resources/css/components/forms.css index f1fa4ff..11a7587 100644 --- a/resources/css/components/forms.css +++ b/resources/css/components/forms.css @@ -528,3 +528,44 @@ } .author-search-item:hover { background-color: var(--bg3); } + +.game-selector-mode { + display: flex; + gap: 0; + margin-bottom: 15px; + border: 1px solid var(--border); +} + +.game-selector-mode-btn { + display: flex; + align-items: center; + gap: 7px; + padding: 8px 14px; + background: none; + border: none; + border-right: 1px solid var(--border); + color: var(--text2); + font-size: 0.85rem; + cursor: pointer; + font-family: var(--font-family); + transition: background-color 0.1s, color 0.1s; +} + +.game-selector-mode-btn:last-child { + border-right: none; +} + +.game-selector-mode-btn:hover { + background-color: var(--bg3); + color: var(--text); +} + +.game-selector-mode-btn.active { + background-color: var(--bg3); + color: var(--rhpz-orange); + border-bottom: 2px solid var(--rhpz-orange); +} + +.game-selector-platform-only { + grid-column: span 1; +} diff --git a/resources/js/submissions.js b/resources/js/submissions.js index e9b0a67..7931261 100644 --- a/resources/js/submissions.js +++ b/resources/js/submissions.js @@ -22,6 +22,7 @@ const ERROR_TABLE = { uploadError: "One or more files failed to upload.", notAllFilesDone: "Not all the files have finished uploading yet.", noModifications: "Please select at least a type of hack.", + noSystems: "Please select at least a system.", noDescription: "Please provide a description.", noGame: "Please provide a game or create a new one and fill all the required fields.", noLanguages: "Please select at least a language.", @@ -117,6 +118,10 @@ window.SubmissionVerifications = { return verifyCheckboxes( document.querySelector( '#modifications-group' ) ); }, + step5_UtilitiesSystemsCheckboxes: function(){ + return verifyCheckboxes( document.querySelector( '#systems-group' ) ); + }, + /** * Verify if the description field has at least one character. * @returns {boolean} @@ -132,6 +137,10 @@ window.SubmissionVerifications = { */ step7_VerifyGame: function( element ){ + const GAME_SELECTOR_MODE = document.querySelector('input[name="game_selection_mode"]')?.value ?? "game"; + if( GAME_SELECTOR_MODE === 'platform' || GAME_SELECTOR_MODE === 'none' ) + return true; + // Check if we have an already existent selected game. const GAME_ID_INPUT = document.querySelector('input[name="game_id"]'); if( GAME_ID_INPUT ){ @@ -283,12 +292,18 @@ window.Submission = function(){ return false; } - if( SECTION() === "romhacks" ){ + if( SECTION() === "romhacks" || SECTION() === "lua-scripts" ){ console.log( "Step 5" ); if( !SubmissionVerifications.step5_RomhacksModificationsCheckboxes()){ this.errorKey = "noModifications"; return false; } + } else if( SECTION() === "utilities" ){ + console.log( "Step 5" ); + if( !SubmissionVerifications.step5_UtilitiesSystemsCheckboxes()){ + this.errorKey = "noSystems"; + return false; + } } console.log( "Step 6" ); @@ -341,6 +356,7 @@ window.Submission = function(){ notAllFilesDone: 'uploadTarget', uploadError: 'uploadTarget', noModifications: 'modificationsGroup', + noSystems: 'systemsGroup', noDescription: 'descriptionField', noGame: 'gameSelector', noLanguages: 'languagesGroup', diff --git a/resources/views/components/category-selector.blade.php b/resources/views/components/category-selector.blade.php new file mode 100644 index 0000000..2a2193f --- /dev/null +++ b/resources/views/components/category-selector.blade.php @@ -0,0 +1,29 @@ + +
+ +
+ @foreach( $categories as $category ) + + @endforeach +
+ +
diff --git a/resources/views/components/entry-card.blade.php b/resources/views/components/entry-card.blade.php index f1d991d..28ba16c 100644 --- a/resources/views/components/entry-card.blade.php +++ b/resources/views/components/entry-card.blade.php @@ -24,13 +24,26 @@ @foreach( $entry->modifications as $modif ) {{ $modif->name }} @endforeach + @elseif( section_must_be( 'translations', $entry->type ) ) + @foreach( $entry->languages as $lang ) + {{ $lang->name }} + @endforeach + @elseif( section_must_be( 'utilities', $entry->type ) ) + @foreach( $entry->categories as $category ) + {{ $category->name }} + @endforeach @endif @if( $entry->status_id ) {{ $entry->status->name }} @endif - @foreach( $entry->languages as $lang ) - {{ $lang->name }} - @endforeach + @if( $entry->level_id ) + {{ $entry->level->name }} + @endif + @if( section_must_not_be( 'translations', $entry->type ) ) + @foreach( $entry->languages as $lang ) + {{ $lang->name }} + @endforeach + @endif
x diff --git a/resources/views/entries/show.blade.php b/resources/views/entries/show.blade.php index 11599e9..102ae5f 100644 --- a/resources/views/entries/show.blade.php +++ b/resources/views/entries/show.blade.php @@ -1,4 +1,4 @@ - + @extends('layouts.app') @section('page-title', $entry->title . " - " . config('app.name') ) @@ -54,9 +54,13 @@
@if($entry->state === 'pending') @can('approve', $entry)
-
+ @csrf @method('PATCH') -
- @@ -145,7 +174,7 @@ @csrf @method('PATCH')
- +