A lot of things.

This commit is contained in:
2026-05-27 21:24:38 +02:00
parent b361f07954
commit d02baa89d6
43 changed files with 1340 additions and 231 deletions

View File

@@ -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);

View File

@@ -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();
});
}
}

View File

@@ -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' ) );
}

View 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.");
}
}

View File

@@ -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.")
};
}
}

View 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]);
}
}

View File

@@ -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);
}

View File

@@ -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');
}
/**

View File

@@ -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 );
}
/**

View File

@@ -32,6 +32,7 @@ class Entry extends Model
'youtube_link',
'user_id',
'complete_title',
'comments_thread_id',
];
/**

View File

@@ -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 );
}
}

View File

@@ -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 );
}
}

View File

@@ -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;
}
}

View File

@@ -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 );
}
/**

View File

@@ -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();
}
}