Files
RomhackPlaza/app/Services/SubmissionsService.php
2026-06-23 19:24:38 +02:00

1009 lines
36 KiB
PHP

<?php
namespace App\Services;
use App\Exceptions\SubmissionException;
use App\Helpers\EntryHelpers;
use App\Helpers\PlayOnlineHelpers;
use App\Helpers\XenForoHelpers;
use App\Http\Requests\StoreEntryRequest;
use App\Jobs\CreateXenForoCommentsThread;
use App\Jobs\DeleteFile;
use App\Models\Author;
use App\Models\Category;
use App\Models\Entry;
use App\Models\EntryFile;
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;
use Illuminate\Support\Facades\Storage;
/**
* @phpstan-import-type FSFileData from \App\Types\FSTypes
*/
class SubmissionsService {
/**
* Request for store/edit.
* @var Request|null
*/
private ?Request $request = null;
/**
* Section for store/edit.
* @var string|null
*/
private ?string $section = null;
/**
* Entry for edit.
* @var Entry|null
*/
private ?Entry $entry = null;
/**
* @return list<FSFileData>
*/
public function prepareOldFiles( ?Entry $entry = null ): array
{
if( $entry === null ){
$files = old( 'files_uuid', [] );
} else {
$files = old( 'files_uuid', $entry->files->pluck('file_uuid')->toArray() );
}
if( $files === [] )
return [];
$service = app(FileServersService::class);
return array_map(
function( string $uuid ) use ($service) {
$file = EntryFile::where('file_uuid', $uuid)->first();
if( $file )
return [
'name' => $file->filename,
'totalChunks' => 0, // Already uploaded.
'rawFile' => null,
'progressValue' => 0,
'currentChunk' => 0,
'done' => true,
'error' => null,
'uuid' => $uuid,
'state' => $file->state,
'file_explorer' => $service->getArchiveExplorerUrl($file),
'file_explorer_files' => null,
'download_url' => $service->getDownloadFileUrl($file, false),
'can_be_online_patched' => EntryHelpers::enableOnlinePatcherBasedOnExtension($file['filename']),
'meta_online_patcher' => $file->online_patcher,
'meta_secondary_online_patcher' => $file->secondary_online_patcher,
'meta_play_online' => $file->playOnlineSetting()->exists() ? true : false,
'meta_play_online_core' => $file->playOnlineSetting()->exists() ? $file->playOnlineSetting->core : '',
'meta_play_online_threads' => $file->playOnlineSetting()->exists() ? $file->playOnlineSetting->threads : false,
];
$file = Cache::get("uploaded_file_{$uuid}");
if( $file )
return [
'name' => $file['filename'],
'totalChunks' => 0, // Already uploaded.
'rawFile' => null,
'progressValue' => 0,
'currentChunk' => 0,
'done' => true,
'error' => null,
'uuid' => $uuid,
'state' => $file['state'],
'file_explorer' => null,
'file_explorer_files' => null,
'download_url' => null,
'can_be_online_patched' => EntryHelpers::enableOnlinePatcherBasedOnExtension($file['filename']),
'meta_online_patcher' => false,
'meta_secondary_online_patcher' => false,
'meta_play_online' => false,
'meta_play_online_core' => null,
'meta_play_online_threads' => false
];
return null;
},
$files );
}
/**
* @param StoreEntryRequest $request
* @param string $section
*
* @return Entry
* @throws SubmissionException
* @throws \Throwable
*/
public function storeEntry( Request $request, string $section ){
// STEP 1 : Prepare basic fields.
$this->request = $request;
$this->section = $section;
$user_id = \Auth::user()->user_id;
$entry = DB::transaction(function () use ( $user_id ) {
// STEP 2 : Create game.
$gameId = $this->Step2_CreateAndReturnGameId();
// STEP 3 : Create Complete title.
$completeTitle = $this->Step3_BuildCompleteTitle( $gameId );
// STEP 4 : Generate slug and entry title.
$entrySlug = EntryHelpers::uniqueSlug( $completeTitle, Entry::class );
if( section_must_be( 'translations', $this->section ) &&
!$this->request->input('entry_title') ){
$entryTitle = Game::find($gameId)->name ?? "";
} else {
$entryTitle = $this->request->input('entry_title');
}
// STEP 5 : Removed / Delayed.
// $mainImage = $this->Step5_MoveMainImage();
// STEP 6 : Prepare entry fields and save entry.
$fields = [
'type' => $this->section,
'title' => $entryTitle,
'slug' => $entrySlug,
'description' => $this->request->input('description'),
'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'),
'staff_credits' => $this->request->input('staff_credits'),
'relevant_link' => $this->request->input('release_site'),
'youtube_link' => $this->request->input('youtube_video'),
'user_id' => $user_id,
'complete_title' => $completeTitle,
'level_id' => $this->request->input('level')
];
$entry = Entry::create( $fields );
// STEP 7 : Save entry fields.
$this->Step7_SaveEntryFiles( $entry );
// STEP 8 : Save hashes.
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','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', 'documents'], $this->section ) ) {
$this->Step11_5_SaveCategories($entry);
}
// STEP 12 : Prepare Gallery images.
$this->Step12a_PrepareGalleryImages( $entry );
return $entry;
});
// Step 12, Move main image and gallery.
$this->Step12b_MoveMainImage( $entry );
$this->Step12c_SaveGalleryImages( $entry );
// Step 13: Try to create the comments section.
$this->Step13_CreateCommentsThread( $entry );
// Step 14: Refresh XF count.
if( $entry->state !== 'draft')
XenForoHelpers::updateEntriesCount( $entry->user_id );
return $entry;
}
/**
* @return null|int
*
* @throws SubmissionException
*/
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');
// No fields like a draft.
if( !$this->request->input('new-game-title') &&
!$this->request->input('new-game-platform') &&
!$this->request->input('new-game-genre') )
return null;
// Need to create a game.
$game = $this->createGameFromFormFields();
return $game->id;
}
private function createGameFromFormFields(): Game
{
if( !$this->request->input('new-game-title') || !$this->request->input('new-game-platform') || !$this->request->input('new-game-genre') )
throw new SubmissionException( "New game informations is missing" );
$platform = Platform::find( $this->request->input('new-game-platform') );
$genre = Genre::find( $this->request->input('new-game-genre') );
if( !$platform || !$genre )
throw new SubmissionException( "Incorrect game platform id" );
$gameSlug = EntryHelpers::uniqueSlug( $this->request->input('new-game-title'), Game::class );
return Game::create([
'name' => trim( $this->request->input('new-game-title') ),
'slug' => $gameSlug,
'platform_id' => $platform->id,
'genre_id' => $genre->id,
]);
}
/**
* Prepare and build complete title.
*
* @param int|null $gameId
*
* @return string
*/
private function Step3_BuildCompleteTitle( ?int $gameId = null ): string {
$fields = [];
$fields['entry_title'] = $this->request->input('entry_title') ?? null;
if( section_must_be( [ 'homebrew', 'translations' ], $this->section ) && $gameId ){
$fields['game_name'] = $gameId ? Game::find( $gameId )->name : null;
}
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'], $this->section ) ) {
// TODO: Add single platform ID compatibility.
$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 );
}
/**
* @param int $entryId
*
* @return void
* @throws SubmissionException
*/
private function Step7_SaveEntryFiles( Entry $entry, ?array $uuidData = null ): void
{
if( !$uuidData )
$uuidData = $this->request->input('files_uuid', [] );
$metadataArray = $this->request->input('files_metadata', []);
foreach ( $uuidData ?? [] as $uuid ) {
$fileData = Cache::pull("uploaded_file_{$uuid}");
if( !$fileData )
throw new SubmissionException( "File {$uuid} has expired. Please delete all your files and retry. If it's an edition, delete all the new files and retry." );
if( section_must_be( [ 'romhacks', 'translations' ], $entry->type ) ) {
$onlinePatcher = (bool)($metadataArray[$uuid]['online_patcher'] ?? false);
if (!$onlinePatcher)
$onlinePatcher = EntryHelpers::enableOnlinePatcherBasedOnExtension($fileData['filename']);
$secondaryOnlinePatcher = (bool)($metadataArray[$uuid]['secondary_online_patcher'] ?? false);
} else {
$onlinePatcher = false;
$secondaryOnlinePatcher = false;
}
$file = EntryFile::create([
'entry_id' => $entry->id,
'file_uuid' => $uuid,
'filename' => $fileData['filename'],
'filepath' => $fileData['filepath'],
'favorite_server' => $fileData['favorite_server'],
'favorite_at' => \DateTimeImmutable::createFromTimestamp( $fileData['favorite_at'] ),
'filesize' => $fileData['filesize'],
'state' => 'public',
'online_patcher' => $onlinePatcher,
'secondary_online_patcher' => $secondaryOnlinePatcher,
]);
if( section_must_be( ['romhacks', 'translations', 'homebrew'], $entry->type ) ) {
$playOnline = (bool)($metadataArray[$uuid]['play_online'] ?? false);
$playOnlineCore = $metadataArray[$uuid]['play_online_core'] ?? null;
$playOnlineThreads = (bool)($metadataArray[$uuid]['play_online_threads'] ?? false);
if (!$playOnline && $entry->getRealPlatform()?->play_online_core !== null) {
$playOnline = true;
$playOnlineCore = $entry->getRealPlatform()?->play_online_core;
}
if ($playOnline) {
$file->playOnlineSetting()->updateOrCreate(
['file_id' => $file->id],
[
'core' => $playOnlineCore,
'threads' => $playOnlineThreads,
]
);
}
}
}
}
/**
* @param int $entryId
*
* @return void
*/
private function Step8_SaveHashes( int $entryId ): void
{
foreach ( $this->request->input('hashes', [] ) ?? [] as $hash ) {
if( !isset($hash['filename'], $hash['hash_crc32'], $hash['hash_sha1'], $hash['verified']) ) {
continue;
}
EntryHash::create([
'entry_id' => $entryId,
'filename' => $hash['filename'],
'hash_crc32' => $hash['hash_crc32'],
'hash_sha1' => $hash['hash_sha1'],
'verified' => $hash['verified'],
]);
}
}
/**
* @param Entry $entry
*
* @return void
* @throws SubmissionException
*/
private function Step9_SaveAuthors( Entry $entry ): void
{
// TODO: Code fragment to be replaced by edit version.
// Existing authors.
foreach ( $this->request->input('authors', [] ) ?? [] as $authorId ) {
$author = Author::find( $authorId );
if( !$author )
throw new SubmissionException( "Author {$authorId} does not exist." );
$entry->authors()->attach( $author->id );
}
// New Authors
foreach ( $this->request->input('new-authors', [] ) ?? [] as $authorName ) {
$authorName = trim( $authorName );
if( $authorName === '' )
continue;
$author = Author::firstOrCreate(
['slug' => EntryHelpers::uniqueSlug( $authorName, Author::class )],
['name' => $authorName]
);
$entry->authors()->attach( $author->id );
}
}
/**
* @param Entry $entry
*
* @return void
* @throws SubmissionException
*/
private function Step10_SaveRomhacksModifications( Entry $entry ): void
{
// TODO: Replace by edit version
foreach ( $this->request->input('modifications', [] ) ?? [] as $modificationId ) {
$modification = Modification::find( $modificationId );
if( !$modification )
throw new SubmissionException( "Modification {$modificationId} does not exist." );
$entry->modifications()->attach( $modification->id );
}
}
/**
* @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
*
* @return void
* @throws SubmissionException
*/
private function Step11_SaveLanguages( Entry $entry ): void
{
// TODO: Replace by edit version.
foreach ( $this->request->input('languages', [] ) ?? [] as $languageId ) {
$language = Language::find( $languageId );
if( !$language )
throw new SubmissionException( "Language {$languageId} does not exist." );
$entry->languages()->attach( $language->id );
}
}
/**
* @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 $i => $imagePath ) {
$entry->gallery()->create([
'image' => $imagePath,
'order' => $i
]);
}
}
/**
* @param Entry $entry
*
* @return void
*/
private function Step12b_MoveMainImage( Entry $entry ): void {
$mainImage = $entry->main_image;
if( !$mainImage )
return;
$newPath = 'entries/main-images/' . basename($mainImage);
if( !Storage::disk('public')->move($mainImage, $newPath) )
return;
$entry->update(['main_image' => $newPath]);
}
private function Step12c_SaveGalleryImages( Entry $entry ): void
{
foreach ( $entry->gallery ?? [] as $galleryItem ) {
$newPath = 'entries/gallery-images/' . $entry->id . '/' . basename($galleryItem->image);
if( !Storage::disk('public')->move($galleryItem->image, $newPath) )
continue;
$galleryItem->update(['image' => $newPath]);
}
}
public function editEntry(Request $request, string $section, Entry $entry ): Entry
{
// STEP 1: Prepare basic fields and keep in save some others fields.
$this->request = $request;
$this->section = $section;
$this->entry = $entry;
if( \Auth::user()->can('moderate', $entry) ){
$user_id = $this->request->input('owner_user_id');
$oldUserId = $this->entry->user_id;
} else {
$user_id = \Auth::user()->user_id;
$oldUserId = null;
}
$oldMainImage = $entry->main_image;
$galleryPaths = [];
$entry = DB::transaction( function() use ( $user_id, &$galleryPaths ){
// STEP 2: Create game if different.
$gameId = null;
$gameId = $this->eStep2_VerifyCreateAndEditGameId();
// STEP 3: Recreate complete title and refresh slug if needed.
$completeTitle = $this->Step3_BuildCompleteTitle( $gameId );
if( $completeTitle !== $this->entry->complete_title ) {
$this->entry->complete_title = $completeTitle;
$this->entry->slug = EntryHelpers::uniqueSlug( $completeTitle, Entry::class, $this->entry->id );
}
// STEP 4: Regenerate entry title.
if( section_must_be( 'translations', $this->section ) &&
!$this->request->input('entry_title') ){
$this->entry->title = Game::find($gameId)->name;
} else {
$this->entry->title = $this->request->input('entry_title');
}
// STEP 5: Update entry fields.
$fields = [
'type' => $this->section,
'title' => $this->entry->title, // Useless, I know.
'slug' => $this->entry->slug,
'description' => $this->request->input('description'),
'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'),
'staff_credits' => $this->request->input('staff_credits'),
'relevant_link' => $this->request->input('release_site'),
'youtube_link' => $this->request->input('youtube_video'),
'user_id' => $user_id,
'complete_title' => $completeTitle,
'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;
if( $fields['featured'] == true && $this->entry->featured_at === null )
$fields['featured_at'] = now();
if( $fields['featured'] == false )
$fields['featured_at'] = null;
$fields['comments_thread_id'] = $this->request->input('comments_thread_id');
$refresh_created_at = $this->request->input('refresh_created_at') ?? false;
if( $refresh_created_at )
$fields['created_at'] = now();
}
$this->entry->update( $fields );
// STEP 6: Update entry files.
$this->eStep6_UpdateEntryFiles( $this->entry->id );
// STEP 7: Update hashes.
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', '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', 'documents'], $this->section ) )
$this->eStep10_5_UpdateCategories();
// STEP 11: Prepare new gallery images and prepare deletion of others ones.
$galleryPaths = $this->eStep11a_UpdateGalleryImages();
return $this->entry;
});
// STEP 11 : Update main image if needed.
$this->eStep11b_UpdateMainImage( $oldMainImage );
// STEP 11 : Update gallery storage.
$this->eStep11c_UpdateGalleryImages( $galleryPaths );
// STEP 12: Refresh XF count.
if( $entry->state !== 'draft' ) {
if ($oldUserId)
XenForoHelpers::updateEntriesCount($oldUserId);
XenForoHelpers::updateEntriesCount($entry->user_id);
}
// STEP 13: Try to create comments area if it doesn't exist.
$this->Step13_CreateCommentsThread( $this->entry );
return $entry;
}
/**
* @throws SubmissionException
*/
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') ){
if( $this->entry->game_id == $this->request->input('game_id') ){
return $this->entry->game_id; // No changes.
} else { // Change in game but already exist.
$game = Game::find( $this->request->input('game_id') );
if( !$game )
throw new SubmissionException( "Game {$this->request->input('game_id')} does not exist." );
$this->entry->game_id = $game->id;
return $this->entry->game_id;
}
}
// In draft.
if( !$this->request->input('new-game-title') &&
!$this->request->input('new-game-platform') &&
!$this->request->input('new-game-genre') )
return $this->entry->game_id;
// Need to create a game.
$game = $this->createGameFromFormFields();
$this->entry->game_id = $game->id;
return $this->entry->game_id;
}
/**
* @throws SubmissionException
*/
private function eStep6_UpdateEntryFiles(int $entryId ): void
{
$requestUuids = $this->request->input('files_uuid', []) ?? [];
$requestStates = $this->request->input('files_state', []) ?? [];
$existingUuids = EntryFile::where( 'entry_id', $entryId )->pluck('file_uuid')->toArray();
$needDeletion = array_diff( $existingUuids, $requestUuids );
if( !empty( $needDeletion ) ){
$userId = \Auth::user()->user_id;
EntryFile::where('entry_id', $entryId)->whereIn('file_uuid', $needDeletion)->whereNot('state', 'archived')->get()->each( function ( $f ) use ( $userId ) {
DeleteFile::dispatch( $f->filepath, $f->filename, $userId);
});
EntryFile::where('entry_id', $entryId)->whereIn('file_uuid', $needDeletion)->whereNot('state', 'archived')->delete();
}
$needAddition = array_diff( $requestUuids, $existingUuids );
if( !empty( $needAddition ) ){
$this->Step7_SaveEntryFiles( $this->entry, $needAddition ); // Same code.
}
$metadataArray = $this->request->input('files_metadata', []);
$stateMap = array_combine( $requestUuids, $requestStates );
foreach( $stateMap as $uuid => $state ){
if( section_must_be( ['romhacks', 'translations'], $this->entry->type ) ) {
$onlinePatcher = (bool)($metadataArray[$uuid]['online_patcher'] ?? false);
$secondaryOnlinePatcher = (bool)($metadataArray[$uuid]['secondary_online_patcher'] ?? false);
} else {
$onlinePatcher = false;
$secondaryOnlinePatcher = false;
}
$entryFile = EntryFile::where('file_uuid', $uuid)->where('entry_id', $entryId)->where('state', '!=', 'archived')->first();
if( !$entryFile )
continue;
$entryFile->update([
'state' => $state,
'online_patcher' => $onlinePatcher,
'secondary_online_patcher' => $secondaryOnlinePatcher,
]);
if( section_must_be( ['romhacks', 'translations', 'homebrew'], $this->entry->type ) ) {
$playOnline = (bool)($metadataArray[$uuid]['play_online'] ?? false);
$playOnlineCore = $metadataArray[$uuid]['play_online_core'] ?? null;
$playOnlineThreads = (bool)($metadataArray[$uuid]['play_online_threads'] ?? false);
if ($playOnline) {
if ($playOnlineCore === null || !in_array($playOnlineCore, PlayOnlineHelpers::getCoreLists()))
$playOnlineCore = $this->entry->getRealPlatform()->play_online_core ? $this->entry->getRealPlatform()->play_online_core : 'nes';
$entryFile->playOnlineSetting()->updateOrCreate(
['file_id' => $entryFile->id],
[
'core' => $playOnlineCore,
'threads' => $playOnlineThreads,
]
);
} else {
$entryFile->playOnlineSetting()->delete();
}
}
}
}
private function eStep7_UpdateHashes(int $entryId): void
{
$requestHashes = collect( $this->request->input('hashes', [] ) )
->filter( fn($h) => isset( $h['filename'], $h['hash_crc32'], $h['hash_sha1'], $h['verified'] ) )
->keyBy( 'hash_sha1' )
->toArray();
;
$existingHashes = EntryHash::where( 'entry_id', $entryId )->get()->keyBy( 'hash_sha1' );
$hashsToDelete = array_diff( $existingHashes->keys()->toArray(), array_keys( $requestHashes ) );
if( !empty( $hashsToDelete ) ){
EntryHash::where( 'entry_id', $entryId )->whereIn('hash_sha1', $hashsToDelete)->delete();
}
foreach( $requestHashes as $sha1 => $hash ){
if( $existingHashes->has( $sha1 ) ){
$existingHashes->get( $sha1 )->update([
'filename' => $hash['filename'],
'hash_crc32' => $hash['hash_crc32'],
'hash_sha1' => $hash['hash_sha1'],
'verified' => $hash['verified'],
]);
} else {
EntryHash::create([
'entry_id' => $entryId,
'filename' => $hash['filename'],
'hash_crc32' => $hash['hash_crc32'],
'hash_sha1' => $hash['hash_sha1'],
'verified' => $hash['verified'],
]);
}
}
}
/**
* @return void
* @throws SubmissionException
*/
private function eStep8_UpdateAuthors(): void
{
$syncAuthorsId = [];
$requestAuthorsId = $this->request->input('authors', [] ) ?? [];
if( !empty( $requestAuthorsId ) ){
$valid = Author::whereIn( 'id', $requestAuthorsId )->pluck('id')->toArray();
if( count( $valid ) !== count( $requestAuthorsId ) ){
throw new SubmissionException( "One of the authors doesn't exist." );
}
$syncAuthorsId = array_merge( $syncAuthorsId, $requestAuthorsId );
}
foreach ( $this->request->input('new-authors', [] ) ?? [] as $authorName ) {
$authorName = trim($authorName);
if ($authorName === '')
continue;
$author = Author::firstOrCreate(
['slug' => EntryHelpers::uniqueSlug($authorName, Author::class)],
['name' => $authorName]
);
$syncAuthorsId[] = $author->id;
}
$this->entry->authors()->sync( $syncAuthorsId );
}
/**
* @return void
* @throws SubmissionException
*/
private function eStep9_UpdateRomhacksModifications(): void
{
$requestModifications = $this->request->input('modifications', [] ) ?? [];
if( !empty( $requestModifications ) ){
$valid = Modification::whereIn( 'id', $requestModifications )->pluck('id')->toArray();
if( count( $valid ) !== count( $requestModifications ) ){
throw new SubmissionException( "One of the modifications doesn't exist." );
}
}
$this->entry->modifications()->sync( $requestModifications );
}
/**
* @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
*/
private function eStep10_UpdateLanguages(): void
{
$requestLanguages = $this->request->input('languages', [] ) ?? [];
if( !empty( $requestLanguages ) ){
$valid = Language::whereIn( 'id', $requestLanguages )->pluck('id')->toArray();
if( count( $valid ) !== count( $requestLanguages ) ){
throw new SubmissionException( "One of the languages doesn't exist." );
}
}
$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', [] ) ?? [];
$existingGalleryPaths = $this->entry->gallery->pluck('image')->toArray();
$needDeletion = array_diff( $existingGalleryPaths, $requestGallery );
if( !empty( $needDeletion ) ){
$this->entry->gallery()->whereIn('image', $needDeletion )->delete();
}
$needAddition = array_diff( $requestGallery, $existingGalleryPaths );
$images = [];
foreach( $needAddition as $imagePath ){
$images[] = $this->entry->gallery()->create([
'image' => $imagePath,
]);
}
foreach ( $requestGallery as $i => $imagePath ){
$this->entry->gallery()->where('image', $imagePath )->update(['order' => $i]);
}
return [ 'addition' => $images, 'deletion' => $needDeletion ];
}
private function eStep11b_UpdateMainImage( ?string $oldMainImagePath ): void
{
$currentMainImagePath = $this->entry->main_image;
if( $currentMainImagePath === $oldMainImagePath )
return;
if( !$currentMainImagePath ) {
if( $oldMainImagePath && Storage::disk('public')->exists($oldMainImagePath) )
Storage::disk('public')->delete($oldMainImagePath);
return;
}
$newPath = 'entries/main-images/' . basename( $currentMainImagePath );
if( !Storage::disk('public')->move( $currentMainImagePath, $newPath ) ){
$this->entry->update(['main_image' => $oldMainImagePath]);
return;
}
$this->entry->update(['main_image' => $newPath]);
if( $oldMainImagePath && Storage::disk('public')->exists($oldMainImagePath) )
Storage::disk('public')->delete($oldMainImagePath);
}
private function eStep11c_UpdateGalleryImages( array $pathsChanges ): void
{
foreach ( $pathsChanges['deletion'] as $deletePath ){
if( Storage::disk('public')->exists($deletePath) )
Storage::disk('public')->delete($deletePath);
}
foreach ( $pathsChanges['addition'] as $galleryItem ){
$newPath = 'entries/gallery-images/' . $this->entry->id . '/' . basename( $galleryItem->image );
if( !Storage::disk('public')->move( $galleryItem->image, $newPath ) ){
continue;
}
$galleryItem->update(['image' => $newPath]);
}
}
private function Step13_CreateCommentsThread( Entry $entry ): void
{
if( $entry->state !== 'published' )
return;
if( !$entry->comments_thread_id )
CreateXenForoCommentsThread::dispatch( $entry );
// app(XenforoApiService::class)->createCommentsThread( $entry );
}
}