A lot of things.
This commit is contained in:
@@ -4,9 +4,12 @@ namespace App\Auth;
|
||||
|
||||
use App\Services\XenforoService;
|
||||
use App\XenForoDataTypes\XenForoData;
|
||||
use Illuminate\Contracts\Auth\Access\Authorizable;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
|
||||
class XenForoUser extends XenForoData implements Authenticatable {
|
||||
class XenForoUser extends XenForoData implements Authenticatable, Authorizable {
|
||||
|
||||
use \Illuminate\Foundation\Auth\Access\Authorizable;
|
||||
|
||||
public ?array $permissions = null;
|
||||
public function getAuthIdentifierName(): string
|
||||
@@ -64,7 +67,7 @@ class XenForoUser extends XenForoData implements Authenticatable {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function can(string $permissionGroup, string $permissionName): bool
|
||||
public function _can(string $permissionGroup, string $permissionName): bool
|
||||
{
|
||||
if( !$this->permissions ){
|
||||
$this->permissions = $this->services->getPermissions($this->data->user_id, $this->data->permission_combination_id);
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
use App\Models\Entry;
|
||||
use App\Services\XenforoApiService;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class EntryHelpers {
|
||||
@@ -56,4 +59,33 @@ class EntryHelpers {
|
||||
default => $fields['entry_title'],
|
||||
};
|
||||
}
|
||||
|
||||
public static function getLatestComments(Entry $entry, int $limit = 20): array {
|
||||
|
||||
if( !$entry->comments_thread_id ){
|
||||
return [];
|
||||
}
|
||||
|
||||
$cacheKey = "entry_comments_{$entry->id}";
|
||||
return Cache::remember($cacheKey, now()->addDays(1), function () use ($entry, $limit) {
|
||||
|
||||
$service = app(XenforoApiService::class);
|
||||
|
||||
// Get thread infos and pagination.
|
||||
$paginationInfos = $service->getThreadPosts($entry->comments_thread_id, 1);
|
||||
$lastPage = $paginationInfos['pagination']['last_page'] ?? 1;
|
||||
|
||||
// Get last threads
|
||||
$lastPageData = $lastPage > 1 ? $service->getThreadPosts($entry->comments_thread_id, $lastPage) : $paginationInfos;
|
||||
$posts = $lastPageData['posts'] ?? [];
|
||||
|
||||
if( count( $posts ) < $limit && $lastPage > 1 ){
|
||||
$previousPageData = $service->getThreadPosts($entry->comments_thread_id, $lastPage - 1 );
|
||||
$posts = array_merge( $posts, $previousPageData['posts'] ?? [] );
|
||||
}
|
||||
|
||||
return collect( $posts )->slice(-$limit)->reverse()->values()->toArray();
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\EntryHelpers;
|
||||
use App\Models\Entry;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
@@ -25,7 +26,9 @@ class EntryController extends Controller
|
||||
if( $entry->type !== $section )
|
||||
abort(404);
|
||||
|
||||
return view('entries.show', compact('entry', 'section'));
|
||||
$comments = EntryHelpers::getLatestComments( $entry );
|
||||
|
||||
return view('entries.show', compact('entry', 'section', 'comments' ) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
17
app/Http/Controllers/RedirectController.php
Normal file
17
app/Http/Controllers/RedirectController.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Entry;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class RedirectController extends Controller
|
||||
{
|
||||
public function entryReportRedirect( Request $request )
|
||||
{
|
||||
$id = $request->input('id');
|
||||
$entry = Entry::findOrFail($id);
|
||||
|
||||
return redirect()->route('entries.show', ['section' => $entry->type, 'entry' => $entry])->with('success', "Your report has been sent.");
|
||||
}
|
||||
}
|
||||
@@ -102,164 +102,19 @@ class SubmissionController extends Controller
|
||||
|
||||
public function update(StoreEntryRequest $request, string $section, Entry $entry)
|
||||
{
|
||||
if( $entry->type !== $section ) {
|
||||
abort(404);
|
||||
try {
|
||||
$entry = $this->services->editEntry($request, $section, $entry);
|
||||
|
||||
return match ($entry->state) {
|
||||
'published' => redirect()->route('entries.show', ['section' => $section, 'entry' => $entry->slug])->with('success', "Your entry has been published."),
|
||||
'pending' => redirect()->route('home')->with('success', "Your entry has been submitted and is pending review."),
|
||||
default => redirect()->route('home')->with('success', "Your entry has been saved as a draft.")
|
||||
};
|
||||
} catch ( SubmissionException $e ) {
|
||||
return back()->withInput()->withErrors(['error' => $e->getMessage()]);
|
||||
} catch ( \Exception $e ) {
|
||||
return back()->withInput()->withErrors(['error' => 'Unknown error: '.$e->getMessage()]);
|
||||
}
|
||||
|
||||
$gameId = null;
|
||||
if( !$request->input('game_id') ){
|
||||
if( $request->input('new-game-title') && $request->input('new-game-platform') && $request->input('new-game-genre') ){
|
||||
$platform = Platform::find($request->input('new-game-platform'));
|
||||
$genre = Genre::find($request->input('new-game-genre'));
|
||||
$game = Game::create([
|
||||
'name' => $request->input('new-game-title'),
|
||||
'slug' => Str::slug($request->input('new-game-title')),
|
||||
'platform_id' => $platform->id,
|
||||
'genre_id' => $genre->id,
|
||||
]);
|
||||
$gameId = $game->id;
|
||||
}
|
||||
} else {
|
||||
$gameId = $request->input('game_id');
|
||||
}
|
||||
|
||||
$mainImage = $entry->main_image;
|
||||
if ( $request->hasFile('main-image') ) {
|
||||
if ( $mainImage ) {
|
||||
Storage::disk('public')->delete($mainImage);
|
||||
}
|
||||
$mainImage = $request->file('main-image')->store('entries/main_images', 'public');
|
||||
} elseif ( $request->input('remove_main_image') === '1' ) {
|
||||
if ( $mainImage ) {
|
||||
Storage::disk('public')->delete($mainImage);
|
||||
}
|
||||
$mainImage = null;
|
||||
}
|
||||
|
||||
$staffCredits = collect($request->input('credits', []))
|
||||
->filter(fn($item) => isset($item['name']) || isset($item['description']))
|
||||
->map(function ($item) {
|
||||
$name = trim($item['name'] ?? '');
|
||||
$description = trim($item['description'] ?? '');
|
||||
if ($name === '' && $description === '') {
|
||||
return null;
|
||||
}
|
||||
return trim($name . ($name !== '' && $description !== '' ? ' — ' : '') . $description);
|
||||
})
|
||||
->filter()
|
||||
->implode("\n");
|
||||
|
||||
$fields = [
|
||||
'type' => $section,
|
||||
'title' => $request->input('entry_title'),
|
||||
'slug' => $request->input('slug') ?? Str::slug($request->input('entry_title', '')),
|
||||
'description' => $request->input('description'),
|
||||
'main_image' => $mainImage,
|
||||
'state' => $request->input('submit-state', 'draft'),
|
||||
'game_id' => $gameId,
|
||||
'status_id' => $request->input('status'),
|
||||
'version' => $request->input('version'),
|
||||
'release_date' => $request->input('release-date'),
|
||||
'staff_credits' => $staffCredits ?: null,
|
||||
'relevant_link' => $request->input('release_site'),
|
||||
'youtube_link' => $request->input('youtube_video'),
|
||||
];
|
||||
|
||||
$entry->update($fields);
|
||||
|
||||
$entry->hashes()->delete();
|
||||
foreach ( $request->input('hashes', []) as $hash ) {
|
||||
if( !isset($hash['filename'], $hash['crc32'], $hash['sha1'], $hash['verified']) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EntryHash::create([
|
||||
'entry_id' => $entry->id,
|
||||
'filename' => $hash['filename'],
|
||||
'hash_crc32' => $hash['crc32'],
|
||||
'hash_sha1' => $hash['sha1'],
|
||||
'verified' => $hash['verified'],
|
||||
]);
|
||||
}
|
||||
|
||||
$authorIds = [];
|
||||
foreach ( $request->input('authors', []) as $authorId ) {
|
||||
$author = Author::find($authorId);
|
||||
if( $author ) {
|
||||
$authorIds[] = $author->id;
|
||||
}
|
||||
}
|
||||
foreach( $request->input('new-authors', []) as $authorName ) {
|
||||
$authorName = trim($authorName);
|
||||
if ($authorName === '') continue;
|
||||
|
||||
$author = Author::firstOrCreate(
|
||||
['slug' => Str::slug($authorName)],
|
||||
['name' => $authorName],
|
||||
);
|
||||
$authorIds[] = $author->id;
|
||||
}
|
||||
$entry->authors()->sync(array_values(array_unique($authorIds)));
|
||||
|
||||
if( section_must_be( 'romhacks', $section ) ){
|
||||
$entry->modifications()->sync($request->input('modifications', []));
|
||||
} else {
|
||||
$entry->modifications()->sync([]);
|
||||
}
|
||||
|
||||
$entry->languages()->sync($request->input('languages', []));
|
||||
|
||||
$existingFileUuids = $request->input('existing_file_ids', []);
|
||||
if (!is_array($existingFileUuids)) {
|
||||
$existingFileUuids = [];
|
||||
}
|
||||
$entry->files()->whereNotIn('file_uuid', $existingFileUuids)->delete();
|
||||
|
||||
foreach ( $request->input('file_ids', []) as $file_uuid ) {
|
||||
$fileData = Cache::pull("uploaded_file_{$file_uuid}");
|
||||
if( ! $fileData ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EntryFile::create([
|
||||
'entry_id' => $entry->id,
|
||||
'file_uuid' => $fileData['uuid'],
|
||||
'filename' => $fileData['filename'],
|
||||
'filepath' => $fileData['filepath'],
|
||||
'favorite_server' => $fileData['favorite_server'],
|
||||
'favorite_at' => \DateTimeImmutable::createFromTimestamp( $fileData['favorite_at'] ),
|
||||
'filesize' => $fileData['filesize'],
|
||||
'state' => 'public'
|
||||
]);
|
||||
}
|
||||
|
||||
$existingGalleryIds = $request->input('existing_gallery_ids', []);
|
||||
if (!is_array($existingGalleryIds)) {
|
||||
$existingGalleryIds = [];
|
||||
}
|
||||
$entry->gallery()->whereNotIn('id', $existingGalleryIds)->get()->each(function ($gallery) {
|
||||
if ($gallery->image) {
|
||||
Storage::disk('public')->delete($gallery->image);
|
||||
}
|
||||
$gallery->delete();
|
||||
});
|
||||
|
||||
foreach ( $request->file('gallery', [] ) as $galleryFile ){
|
||||
if( !$galleryFile->isValid() ){
|
||||
continue;
|
||||
}
|
||||
|
||||
$path = $galleryFile->store('entries/gallery/' . $entry->id, 'public');
|
||||
EntryGallery::create([
|
||||
'entry_id' => $entry->id,
|
||||
'image' => $path
|
||||
]);
|
||||
}
|
||||
|
||||
return match( $entry->state ){
|
||||
'published' => redirect()->route('entries.show', [ 'section' => $section, 'entry' => $entry->slug ])->with('success', "Your entry has been published."),
|
||||
'pending' => redirect()->route('home')->with('success', "Your entry has been submitted and is pending review."),
|
||||
default => redirect()->route('home')->with('success', "Your entry has been saved as a draft.")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
29
app/Http/Controllers/WebhookController.php
Normal file
29
app/Http/Controllers/WebhookController.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Entry;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class WebhookController extends Controller
|
||||
{
|
||||
|
||||
public function XenForoNewPost(Request $request)
|
||||
{
|
||||
|
||||
if( $request->header('XF-Webhook-Secret') !== env('WEBHOOK_SECRET') )
|
||||
abort(403);
|
||||
|
||||
$threadId = $request->input('data.thread_id');
|
||||
if( $threadId ){
|
||||
$entry = Entry::where('comments_thread_id', $threadId)->first();
|
||||
if( $entry ){
|
||||
Cache::forget("entry_comments_{$entry->id}");
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ class CheckXenForoPermissions
|
||||
foreach ($permissions as $permissionStr) {
|
||||
[$group, $permission] = explode('.', $permissionStr);
|
||||
|
||||
if( !\Auth::user()->can($group, $permission) )
|
||||
if( !\Auth::user()->_can($group, $permission) )
|
||||
return $this->deny($request, $permission);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,11 @@ class StoreEntryRequest extends FormRequest
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
// TODO: Change it by role.
|
||||
return true;
|
||||
$entry = $this->route('entry');
|
||||
if( $entry )
|
||||
return $this->user()->can('update', $entry);
|
||||
|
||||
return $this->user()->can('create', '\App\Models\Entry');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Services\TemporaryFileService;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
@@ -12,7 +13,7 @@ class TemporaryFileUploadRequest extends FormRequest
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
return $this->user()->can('create', TemporaryFileService::class );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,6 +32,7 @@ class Entry extends Model
|
||||
'youtube_link',
|
||||
'user_id',
|
||||
'complete_title',
|
||||
'comments_thread_id',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Auth\XenForoGuard;
|
||||
use App\Policies\TempFilePolicy;
|
||||
use App\Services\TemporaryFileService;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
@@ -24,5 +26,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
\Auth::extend('xenforo', function ($app, $name, array $config) {
|
||||
return new XenForoGuard($app['request']);
|
||||
});
|
||||
|
||||
\Gate::policy(TemporaryFileService::class, TempFilePolicy::class );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Services;
|
||||
use App\Exceptions\SubmissionException;
|
||||
use App\Helpers\EntryHelpers;
|
||||
use App\Http\Requests\StoreEntryRequest;
|
||||
use App\Jobs\CreateXenForoCommentsThread;
|
||||
use App\Models\Author;
|
||||
use App\Models\Entry;
|
||||
use App\Models\EntryFile;
|
||||
@@ -36,6 +37,12 @@ class SubmissionsService {
|
||||
*/
|
||||
private ?string $section = null;
|
||||
|
||||
/**
|
||||
* Entry for edit.
|
||||
* @var Entry|null
|
||||
*/
|
||||
private ?Entry $entry = null;
|
||||
|
||||
/**
|
||||
* @return list<FSFileData>
|
||||
*/
|
||||
@@ -174,6 +181,9 @@ class SubmissionsService {
|
||||
$this->Step12b_MoveMainImage( $entry );
|
||||
$this->Step12c_SaveGalleryImages( $entry );
|
||||
|
||||
// Step 13: Try to create the comments section.
|
||||
$this->Step13_CreateCommentsThread( $entry );
|
||||
|
||||
return $entry;
|
||||
|
||||
}
|
||||
@@ -190,6 +200,13 @@ class SubmissionsService {
|
||||
return $this->request->input('game_id');
|
||||
|
||||
// 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" );
|
||||
|
||||
@@ -201,14 +218,12 @@ class SubmissionsService {
|
||||
|
||||
$gameSlug = EntryHelpers::uniqueSlug( $this->request->input('new-game-title'), Game::class );
|
||||
|
||||
$game = Game::create([
|
||||
return Game::create([
|
||||
'name' => trim( $this->request->input('new-game-title') ),
|
||||
'slug' => $gameSlug,
|
||||
'platform_id' => $platform->id,
|
||||
'genre_id' => $genre->id,
|
||||
]);
|
||||
|
||||
return $game->id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,7 +243,7 @@ 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', 'homebrew', 'lua-scripts', 'tutorials'], $this->section ) ) {
|
||||
if( section_must_be(['romhacks', 'translations', 'homebrew', 'lua-scripts', 'tutorials'], $this->section ) ) {
|
||||
// TODO: Add single platform ID compatibility.
|
||||
$fields['platform_name'] = Game::find( $gameId )->platform->name;
|
||||
}
|
||||
@@ -242,12 +257,15 @@ class SubmissionsService {
|
||||
* @return void
|
||||
* @throws SubmissionException
|
||||
*/
|
||||
private function Step7_SaveEntryFiles( int $entryId ): void
|
||||
private function Step7_SaveEntryFiles( int $entryId, ?array $uuidData = null ): void
|
||||
{
|
||||
foreach ( $this->request->input('files_uuid', [] ) as $uuid ) {
|
||||
if( !$uuidData )
|
||||
$uuidData = $this->request->input('files_uuid', [] );
|
||||
|
||||
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." );
|
||||
throw new SubmissionException( "File {$uuid} has expired. Please delete all your files and retry. If it's an edition, delete all your new files and retry." );
|
||||
|
||||
EntryFile::create([
|
||||
'entry_id' => $entryId,
|
||||
@@ -293,6 +311,8 @@ class SubmissionsService {
|
||||
*/
|
||||
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 );
|
||||
@@ -323,6 +343,9 @@ class SubmissionsService {
|
||||
*/
|
||||
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 )
|
||||
@@ -339,6 +362,8 @@ class SubmissionsService {
|
||||
*/
|
||||
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 )
|
||||
@@ -385,4 +410,325 @@ class SubmissionsService {
|
||||
}
|
||||
}
|
||||
|
||||
public function editEntry( StoreEntryRequest $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;
|
||||
$user_id = 0; // TODO: Replace that.
|
||||
|
||||
$oldMainImage = $entry->main_image;
|
||||
$galleryPaths = [];
|
||||
|
||||
$entry = DB::transaction( function() use ( $user_id, &$galleryPaths ){
|
||||
|
||||
// STEP 2: Create game if different.
|
||||
$gameId = null;
|
||||
if( section_must_be( ['romhacks', 'translations' ], $this->section ) ){
|
||||
$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,
|
||||
'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,
|
||||
];
|
||||
$this->entry->update( $fields );
|
||||
|
||||
// STEP 6: Update entry files.
|
||||
$this->eStep6_UpdateEntryFiles( $this->entry->id );
|
||||
|
||||
// STEP 7: Update hashes.
|
||||
$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 ) ) {
|
||||
$this->eStep9_UpdateRomhacksModifications();
|
||||
}
|
||||
|
||||
// STEP 10: Update Languages.
|
||||
$this->eStep10_UpdateLanguages();
|
||||
|
||||
// STEP 11: Prepare new gallery images and prepare deletion of others ones.
|
||||
$galleryPaths = $this->eStep11a_UpdateGalleryImages();
|
||||
|
||||
// STEP 13: Try to create comments area if it doesn't exist.
|
||||
$this->Step13_CreateCommentsThread( $this->entry );
|
||||
|
||||
return $this->entry;
|
||||
|
||||
});
|
||||
|
||||
// STEP 11 : Update main image if needed.
|
||||
$this->eStep11b_UpdateMainImage( $oldMainImage );
|
||||
|
||||
// STEP 11 : Update gallery storage.
|
||||
$this->eStep11c_UpdateGalleryImages( $galleryPaths );
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SubmissionException
|
||||
*/
|
||||
private function eStep2_VerifyCreateAndEditGameId(): int
|
||||
{
|
||||
// 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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', []);
|
||||
$existingUuids = EntryFile::where( 'entry_id', $entryId )->pluck('file_uuid')->toArray();
|
||||
|
||||
$needDeletion = array_diff( $existingUuids, $requestUuids );
|
||||
if( !empty( $needDeletion ) ){
|
||||
EntryFile::where('entry_id', $entryId)->whereIn('file_uuid', $needDeletion)->delete();
|
||||
}
|
||||
|
||||
$needAddition = array_diff( $requestUuids, $existingUuids );
|
||||
|
||||
if( !empty( $needAddition ) ){
|
||||
$this->Step7_SaveEntryFiles( $this->entry->id, $needAddition ); // Same code.
|
||||
}
|
||||
}
|
||||
|
||||
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 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 );
|
||||
}
|
||||
|
||||
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 ) ){
|
||||
EntryGallery::where('entry_id', $this->entry->id)->whereIn('image', $needDeletion )->delete();
|
||||
}
|
||||
|
||||
$needAddition = array_diff( $requestGallery, $existingGalleryPaths );
|
||||
$images = [];
|
||||
foreach( $needAddition as $imagePath ){
|
||||
$images[] = EntryGallery::create([
|
||||
'entry_id' => $this->entry->id,
|
||||
'image' => $imagePath,
|
||||
]);
|
||||
}
|
||||
|
||||
return [ 'addition' => $images, 'deletion' => $needDeletion ];
|
||||
}
|
||||
|
||||
private function eStep11b_UpdateMainImage( ?string $oldMainImagePath ): void
|
||||
{
|
||||
$currentMainImagePath = $this->entry->main_image;
|
||||
|
||||
if( $currentMainImagePath === $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->comments_thread_id )
|
||||
CreateXenForoCommentsThread::dispatch( $entry );
|
||||
// app(XenforoApiService::class)->createCommentsThread( $entry );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Entry;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
@@ -71,4 +72,40 @@ class XenforoApiService {
|
||||
});
|
||||
}
|
||||
|
||||
public function createCommentsThread( Entry $entry ): bool
|
||||
{
|
||||
if( !$entry->comments_thread_id || $entry->comments_thread_id <= 0 ){
|
||||
$data = [
|
||||
'node_id' => config('xenforo.comments_node_id'),
|
||||
'title' => $entry->complete_title,
|
||||
'message' => $entry->description,
|
||||
'prefix_id' => config('xenforo.comments_prefixes')[$entry->type] ?? 1,
|
||||
'custom_fields' => [ 'entry_id' => $entry->id ],
|
||||
'discussion_open' => true,
|
||||
];
|
||||
|
||||
// TODO: Flag must be removed.
|
||||
$response = $this->post("threads?api_bypass_permissions=true", config('xenforo.bot_user_id'), $data );
|
||||
if( $response['success'] === true ){
|
||||
$commentsThreadId = $response['thread']['thread_id'];
|
||||
$entry->update(['comments_thread_id' => $commentsThreadId]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ConnectionException
|
||||
*/
|
||||
public function getThreadPosts(int $threadId, int $page = 1 ): array
|
||||
{
|
||||
$response = $this->get("threads/{$threadId}/posts?page=$page");
|
||||
if( !isset( $response['posts'] ) || $response['posts'] === [] )
|
||||
return [ 'posts' => [], 'pagination' => null ];
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\View\Components;
|
||||
|
||||
use App\Auth\XenForoUser;
|
||||
use App\Services\XenforoService;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
@@ -13,11 +14,13 @@ class XenForoAvatar extends Component
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public ?XenForoUser $user = null,
|
||||
public null|int|XenForoUser $user = null,
|
||||
)
|
||||
{
|
||||
if( $this->user === null )
|
||||
$this->user = \Auth::user();
|
||||
else if( is_int( $this->user ) )
|
||||
$this->user = app(XenforoService::class)->getXfUser( $this->user );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,3 +11,9 @@ if( !function_exists( 'xfCsrfToken') ){
|
||||
return app(\App\Services\XenforoService::class)->getCSRFToken();
|
||||
}
|
||||
}
|
||||
|
||||
if( !function_exists( 'xfStyleVariationUrl' ) ){
|
||||
function xfStyleVariationUrl( string $variation ): string {
|
||||
return config('app.forum_url') . '/misc/style-variation?variation=' . $variation . "&t=" . xfCsrfToken();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user