Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e7a2a99dba | |||
| c413491171 | |||
| a6d8d924f1 | |||
| c3f13a23af | |||
| 4c84287274 | |||
| 2c1677692f | |||
| ee5b2f6e09 | |||
| fda7aca4f9 | |||
| ed91375514 | |||
| d2a2027c42 | |||
| b422cd2c82 | |||
| f5a90aea4d | |||
| dff376217f | |||
| 55a8154b52 | |||
| 8ca82fd0c8 | |||
| 837b3de163 | |||
| 71014349aa | |||
| aefe03ed67 | |||
| 1afe77415d | |||
| f492a4d02f | |||
| 0c0a6a401e | |||
| bdc4158dec | |||
| 0ecb4610eb | |||
| d75d76ef6e | |||
| dc6df8b02a | |||
| a91106ddf7 | |||
| 53b611bb31 | |||
| 7e1f8fc55a | |||
| f71c51f4ba | |||
| ef24a16132 | |||
| 3b0051585c | |||
| a52db45972 | |||
| 487b2e2956 | |||
| 74a678edda | |||
| dc577f5c8b | |||
| 22893fd957 | |||
| 176a883633 | |||
| 3cb98ce542 | |||
| 300c417f3c | |||
| dcf99a3351 | |||
| 1bc89691d9 | |||
| c76bf45904 | |||
| 3cdb6f88b8 | |||
| 378c34b8c0 | |||
| 73844b6db0 | |||
| f47528a2a1 | |||
| 6977d5ad57 | |||
| 0009d3339e | |||
| 93acbb4325 | |||
| e9fdc9be51 | |||
| a1ca5b084a | |||
| 60dfa1588a | |||
| 60ce1cf400 | |||
| a9158c722a | |||
| 848c528de3 | |||
| d163d24780 | |||
| 05e7d4754f | |||
| 1abfa96c2c |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -30,3 +30,4 @@ avancee.ods
|
|||||||
rhpz.local+1.pem
|
rhpz.local+1.pem
|
||||||
rhpz.local+1-key.pem
|
rhpz.local+1-key.pem
|
||||||
extra.less
|
extra.less
|
||||||
|
config/xenforo.php
|
||||||
|
|||||||
@@ -64,6 +64,19 @@ class XenForoGuard implements Guard
|
|||||||
if ($this->hasUser())
|
if ($this->hasUser())
|
||||||
return $this->user;
|
return $this->user;
|
||||||
|
|
||||||
|
$user = $this->getFromSession();
|
||||||
|
if( $user )
|
||||||
|
return $user;
|
||||||
|
|
||||||
|
$user = $this->getFromCookie();
|
||||||
|
if( $user )
|
||||||
|
return $user;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFromSession(): ?XenForoUser
|
||||||
|
{
|
||||||
$sessionId = $this->request->cookie('xf_session');
|
$sessionId = $this->request->cookie('xf_session');
|
||||||
if(!$sessionId)
|
if(!$sessionId)
|
||||||
return null;
|
return null;
|
||||||
@@ -92,6 +105,64 @@ class XenForoGuard implements Guard
|
|||||||
return $this->user = new XenForoUser($xfUser);
|
return $this->user = new XenForoUser($xfUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function isCorrectCookieKey(string $key, $record): bool
|
||||||
|
{
|
||||||
|
$known = $record->remember_key;
|
||||||
|
if( !$known )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$check = hash('sha256', $key, true);
|
||||||
|
return hash_equals($known, $check);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFromCookie(): ?XenForoUser
|
||||||
|
{
|
||||||
|
$cookie = $this->request->cookie('xf_user');
|
||||||
|
if(!$cookie)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$parts = explode(',', $cookie);
|
||||||
|
if( count( $parts ) !== 2 )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
[$userId, $key] = $parts;
|
||||||
|
$userId = (int) $userId;
|
||||||
|
|
||||||
|
if( !$userId || !$key )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$remembers = \DB::connection('xenforo')
|
||||||
|
->table('user_remember')
|
||||||
|
->where('user_id', $userId)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
if( !$remembers )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$valid = false;
|
||||||
|
|
||||||
|
foreach( $remembers as $remember )
|
||||||
|
{
|
||||||
|
if( $this->isCorrectCookieKey($key, $remember) && $remember->expiry_date >= time() ){
|
||||||
|
$valid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !$valid )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$xfUser = \DB::connection('xenforo')
|
||||||
|
->table('user')
|
||||||
|
->where('user_id', $userId)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if(!$xfUser)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return $this->user = new XenForoUser($xfUser);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unused.
|
* Unused.
|
||||||
*
|
*
|
||||||
|
|||||||
41
app/Console/Commands/MigrateDownloadsCount.php
Normal file
41
app/Console/Commands/MigrateDownloadsCount.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Attributes\Description;
|
||||||
|
use Illuminate\Console\Attributes\Signature;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
#[Signature('migrate:downloads-count')]
|
||||||
|
#[Description('Command description')]
|
||||||
|
class MigrateDownloadsCount extends Command
|
||||||
|
{
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$rows = DB::table('migrations_logs')
|
||||||
|
->where('source_system', 'wp')
|
||||||
|
->where('source_table', 'wp_posts')
|
||||||
|
->where('target_table', 'entries' )
|
||||||
|
->get(['source_id', 'target_id'])
|
||||||
|
;
|
||||||
|
|
||||||
|
$updated = 0;
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$downloadCount = DB::connection('old_wp')
|
||||||
|
->table('postmeta')
|
||||||
|
->where('post_id', $row->source_id)
|
||||||
|
->where('meta_key', 'download_counter' )
|
||||||
|
->value('meta_value');
|
||||||
|
|
||||||
|
if( $downloadCount ) {
|
||||||
|
DB::table('entries')->where('id', $row->target_id)->update(['old_download_count' => intval($downloadCount)]);
|
||||||
|
$updated++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info('All done (' . $updated . '/' . count($rows) .')');
|
||||||
|
}
|
||||||
|
}
|
||||||
22
app/Console/Commands/SubmissionsDown.php
Normal file
22
app/Console/Commands/SubmissionsDown.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Attributes\Description;
|
||||||
|
use Illuminate\Console\Attributes\Signature;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
#[Signature('submissions:down')]
|
||||||
|
#[Description('Disable submissions')]
|
||||||
|
class SubmissionsDown extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Cache::put('submissions_maintenance', true );
|
||||||
|
$this->info("Submissions disabled.");
|
||||||
|
}
|
||||||
|
}
|
||||||
22
app/Console/Commands/SubmissionsUp.php
Normal file
22
app/Console/Commands/SubmissionsUp.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Attributes\Description;
|
||||||
|
use Illuminate\Console\Attributes\Signature;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
#[Signature('submissions:up')]
|
||||||
|
#[Description('Enable submissions')]
|
||||||
|
class SubmissionsUp extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Cache::forget('submissions_maintenance');
|
||||||
|
$this->info('Submissions enabled.');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ use App\Models\Entry;
|
|||||||
use App\Models\EntryFile;
|
use App\Models\EntryFile;
|
||||||
use App\Models\News;
|
use App\Models\News;
|
||||||
use App\Services\XenforoApiService;
|
use App\Services\XenforoApiService;
|
||||||
|
use GuzzleHttp\Exception\ConnectException;
|
||||||
|
use GuzzleHttp\Exception\RequestException;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
@@ -74,8 +76,9 @@ class EntryHelpers {
|
|||||||
$cacheKey = "news_comments_{$entry->id}";
|
$cacheKey = "news_comments_{$entry->id}";
|
||||||
else
|
else
|
||||||
$cacheKey = "entry_comments_{$entry->id}";
|
$cacheKey = "entry_comments_{$entry->id}";
|
||||||
return Cache::remember($cacheKey, now()->addDays(1), function () use ($entry, $limit) {
|
return Cache::flexible($cacheKey, [now()->addMinutes(30), now()->addMinutes(60)], function () use ($entry, $limit) {
|
||||||
|
|
||||||
|
try {
|
||||||
$service = app(XenforoApiService::class);
|
$service = app(XenforoApiService::class);
|
||||||
|
|
||||||
// Get thread infos and pagination.
|
// Get thread infos and pagination.
|
||||||
@@ -86,12 +89,15 @@ class EntryHelpers {
|
|||||||
$lastPageData = $lastPage > 1 ? $service->getThreadPosts($entry->comments_thread_id, $lastPage) : $paginationInfos;
|
$lastPageData = $lastPage > 1 ? $service->getThreadPosts($entry->comments_thread_id, $lastPage) : $paginationInfos;
|
||||||
$posts = $lastPageData['posts'] ?? [];
|
$posts = $lastPageData['posts'] ?? [];
|
||||||
|
|
||||||
if( count( $posts ) < $limit && $lastPage > 1 ){
|
if (count($posts) < $limit && $lastPage > 1) {
|
||||||
$previousPageData = $service->getThreadPosts($entry->comments_thread_id, $lastPage - 1 );
|
$previousPageData = $service->getThreadPosts($entry->comments_thread_id, $lastPage - 1);
|
||||||
$posts = array_merge( $posts, $previousPageData['posts'] ?? [] );
|
$posts = array_merge($posts, $previousPageData['posts'] ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
return collect( $posts )->slice(-$limit)->reverse()->values()->toArray();
|
return collect($posts)->slice(-$limit)->reverse()->values()->toArray();
|
||||||
|
} catch (ConnectException|RequestException $e ){
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -119,4 +125,18 @@ class EntryHelpers {
|
|||||||
session(["downloaded_file_{$entryFile->file_uuid}" => 1]);
|
session(["downloaded_file_{$entryFile->file_uuid}" => 1]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function stripBbCode(?string $text): ?string
|
||||||
|
{
|
||||||
|
if ($text === null) return null;
|
||||||
|
$text = preg_replace('/\[quote\b[^\]]*\](.*?)\[\/quote\]/is', '', $text);
|
||||||
|
return preg_replace('/\[\/?\w+[^\]]*\]/i', '', $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function stripMarkdown(?string $text): ?string
|
||||||
|
{
|
||||||
|
if ($text === null) return null;
|
||||||
|
$html = Str::markdown($text);
|
||||||
|
return html_entity_decode(strip_tags($html), ENT_QUOTES, 'UTF-8');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class AuthorController extends Controller
|
|||||||
{
|
{
|
||||||
$items = Author::withCount('entries')
|
$items = Author::withCount('entries')
|
||||||
->orderBy('name')
|
->orderBy('name')
|
||||||
->tap(fn($query) => $this->applySearch($query, ['name']))
|
->tap(fn($query) => $this->applySearch($query, ['id','name']))
|
||||||
->paginate(30)
|
->paginate(30)
|
||||||
->withQueryString();
|
->withQueryString();
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class GameController extends Controller
|
|||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$items = Game::withCount('entries')->orderBy('name')
|
$items = Game::withCount('entries')->orderBy('name')
|
||||||
->tap(fn($query) => $this->applySearch($query, ['name']))
|
->tap(fn($query) => $this->applySearch($query, ['id','name']))
|
||||||
->paginate(30)->withQueryString();
|
->paginate(30)->withQueryString();
|
||||||
$platforms = Platform::orderBy('name')->get();
|
$platforms = Platform::orderBy('name')->get();
|
||||||
$genres = Genre::orderBy('name')->get();
|
$genres = Genre::orderBy('name')->get();
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class GenreController extends Controller
|
|||||||
{
|
{
|
||||||
$items = Genre::withCount('games')
|
$items = Genre::withCount('games')
|
||||||
->orderBy('name')
|
->orderBy('name')
|
||||||
->tap(fn($query) => $this->applySearch($query, ['name']))
|
->tap(fn($query) => $this->applySearch($query, ['id','name']))
|
||||||
->paginate(30)
|
->paginate(30)
|
||||||
->withQueryString();
|
->withQueryString();
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class LanguageController extends Controller
|
|||||||
{
|
{
|
||||||
$items = Language::withCount('entries')
|
$items = Language::withCount('entries')
|
||||||
->orderBy('name')
|
->orderBy('name')
|
||||||
->tap(fn($query) => $this->applySearch($query, ['name']))
|
->tap(fn($query) => $this->applySearch($query, ['id','name']))
|
||||||
->paginate(30)
|
->paginate(30)
|
||||||
->withQueryString();
|
->withQueryString();
|
||||||
|
|
||||||
|
|||||||
67
app/Http/Controllers/ModCP/LevelController.php
Normal file
67
app/Http/Controllers/ModCP/LevelController.php
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\ModCP;
|
||||||
|
|
||||||
|
use App\Helpers\EntryHelpers;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Level;
|
||||||
|
use App\Traits\ModCPSearch;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class LevelController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
use ModCPSearch;
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$items = Level::withCount('entries')
|
||||||
|
->orderBy('name')
|
||||||
|
->tap(fn($query) => $this->applySearch($query, ['id','name']))
|
||||||
|
->paginate(30)
|
||||||
|
->withQueryString();
|
||||||
|
|
||||||
|
return view('modcp.resources', [
|
||||||
|
'items' => $items,
|
||||||
|
'title' => 'Levels',
|
||||||
|
'singular' => 'Level',
|
||||||
|
'storeRoute' => 'modcp.levels.store',
|
||||||
|
'updateRoute' => 'modcp.levels.update',
|
||||||
|
'destroyRoute' => 'modcp.levels.destroy'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'name' => 'required|string|max:255|unique:levels,name',
|
||||||
|
]);
|
||||||
|
|
||||||
|
Level::create([
|
||||||
|
'name' => trim($request->name),
|
||||||
|
'slug' => EntryHelpers::uniqueSlug( $request->name, Level::class ),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return back()->with('success', 'Level added.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Request $request, Level $level)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'name' => 'required|string|max:255|unique:levels,name,' . $level->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$level->update([
|
||||||
|
'name' => trim($request->name),
|
||||||
|
'slug' => EntryHelpers::uniqueSlug( $request->name, Level::class, $level->id ),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return back()->with('success', 'Level updated.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Level $level)
|
||||||
|
{
|
||||||
|
$level->delete();
|
||||||
|
return back()->with('success', 'Level deleted.');
|
||||||
|
}
|
||||||
|
}
|
||||||
67
app/Http/Controllers/ModCP/ModificationsController.php
Normal file
67
app/Http/Controllers/ModCP/ModificationsController.php
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\ModCP;
|
||||||
|
|
||||||
|
use App\Helpers\EntryHelpers;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Modification;
|
||||||
|
use App\Traits\ModCPSearch;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ModificationsController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
use ModCPSearch;
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$items = Modification::withCount('entries')
|
||||||
|
->orderBy('name')
|
||||||
|
->tap(fn($query) => $this->applySearch($query, ['id','name']))
|
||||||
|
->paginate(30)
|
||||||
|
->withQueryString();
|
||||||
|
|
||||||
|
return view('modcp.resources', [
|
||||||
|
'items' => $items,
|
||||||
|
'title' => 'Modifications',
|
||||||
|
'singular' => 'Modification',
|
||||||
|
'storeRoute' => 'modcp.modifications.store',
|
||||||
|
'updateRoute' => 'modcp.modifications.update',
|
||||||
|
'destroyRoute' => 'modcp.modifications.destroy'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'name' => 'required|string|max:255|unique:modifications,name',
|
||||||
|
]);
|
||||||
|
|
||||||
|
Modification::create([
|
||||||
|
'name' => trim($request->name),
|
||||||
|
'slug' => EntryHelpers::uniqueSlug( $request->name, Modification::class ),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return back()->with('success', 'Modification added.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Request $request, Modification $Modification)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'name' => 'required|string|max:255|unique:modifications,name,' . $Modification->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$Modification->update([
|
||||||
|
'name' => trim($request->name),
|
||||||
|
'slug' => EntryHelpers::uniqueSlug( $request->name, Modification::class, $Modification->id ),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return back()->with('success', 'Modification updated.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Modification $Modification)
|
||||||
|
{
|
||||||
|
$Modification->delete();
|
||||||
|
return back()->with('success', 'Modification deleted.');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ class PlatformController extends Controller
|
|||||||
{
|
{
|
||||||
$items = Platform::withCount(['games','entries'])
|
$items = Platform::withCount(['games','entries'])
|
||||||
->orderBy('name')
|
->orderBy('name')
|
||||||
->tap(fn($query) => $this->applySearch($query, ['name']))
|
->tap(fn($query) => $this->applySearch($query, ['id','name']))
|
||||||
->paginate(30)
|
->paginate(30)
|
||||||
->withQueryString();
|
->withQueryString();
|
||||||
|
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ class SubmissionController extends Controller
|
|||||||
'bg' => '#f3eaff1a',
|
'bg' => '#f3eaff1a',
|
||||||
'border' => '#f3eaff40',
|
'border' => '#f3eaff40',
|
||||||
],
|
],
|
||||||
'lua-script' => [
|
'lua-scripts' => [
|
||||||
'slug' => 'lua-script',
|
'slug' => 'lua-scripts',
|
||||||
'label' => 'Lua script',
|
'label' => 'Lua script',
|
||||||
'icon' => 'terminal',
|
'icon' => 'terminal',
|
||||||
'color' => '#a04515',
|
'color' => '#a04515',
|
||||||
|
|||||||
33
app/Http/Middleware/SubmissionsEnabled.php
Normal file
33
app/Http/Middleware/SubmissionsEnabled.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class SubmissionsEnabled
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param Closure(Request): (Response) $next
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next): Response
|
||||||
|
{
|
||||||
|
if( Cache::get('submissions_maintenance', false ) ){
|
||||||
|
if( $request->expectsJson() ){
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'The submissions are currently in maintenance mode.',
|
||||||
|
], 503 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return back()->withErrors([
|
||||||
|
'maintenance' => "The submissions are currently in maintenance mode.",
|
||||||
|
])->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -175,6 +175,7 @@ class Database extends Component
|
|||||||
public const array SORT_OPTIONS = [
|
public const array SORT_OPTIONS = [
|
||||||
'created_at' => 'Date added',
|
'created_at' => 'Date added',
|
||||||
'release_date' => 'Release date',
|
'release_date' => 'Release date',
|
||||||
|
'total_downloads' => 'Total downloads',
|
||||||
'title' => 'Title'
|
'title' => 'Title'
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -236,7 +237,7 @@ class Database extends Component
|
|||||||
{
|
{
|
||||||
$query = Entry::query()->published()->with([
|
$query = Entry::query()->published()->with([
|
||||||
'game.platform', 'game.genre', 'status', 'authors', 'languages', 'level', 'systems', 'categories', 'modifications'
|
'game.platform', 'game.genre', 'status', 'authors', 'languages', 'level', 'systems', 'categories', 'modifications'
|
||||||
]);
|
])->withSum('files', 'download_count');
|
||||||
|
|
||||||
if( $this->search ) {
|
if( $this->search ) {
|
||||||
$query->where(function($q) {
|
$query->where(function($q) {
|
||||||
@@ -328,7 +329,13 @@ class Database extends Component
|
|||||||
$query->where('user_id', $this->userId);
|
$query->where('user_id', $this->userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $query->orderBy($this->sortBy, $this->sortDir);
|
$sortColumn = $this->sortBy;
|
||||||
|
|
||||||
|
if ($sortColumn === 'total_downloads') {
|
||||||
|
return $query->orderByRaw('COALESCE(files_sum_download_count, 0) + old_download_count ' . $this->sortDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->orderBy($sortColumn, $this->sortDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function searchFilter( string $modelClass, string $search )
|
private function searchFilter( string $modelClass, string $search )
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class EntrySelector extends Component
|
|||||||
if( $entry ) {
|
if( $entry ) {
|
||||||
$this->selectedEntryId = $oldEntryId;
|
$this->selectedEntryId = $oldEntryId;
|
||||||
$this->entryName = $entry->complete_title ?? $entry->title;
|
$this->entryName = $entry->complete_title ?? $entry->title;
|
||||||
|
$this->search = $entry->complete_title ?? $entry->title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use App\Services\XenforoService;
|
|||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Spatie\Activitylog\Models\Concerns\LogsActivity;
|
||||||
|
use Spatie\Activitylog\Support\LogOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -32,6 +34,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||||||
*/
|
*/
|
||||||
class Author extends Model
|
class Author extends Model
|
||||||
{
|
{
|
||||||
|
|
||||||
|
use LogsActivity;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name', 'slug', 'user_id', 'website'
|
'name', 'slug', 'user_id', 'website'
|
||||||
];
|
];
|
||||||
@@ -49,4 +54,14 @@ class Author extends Model
|
|||||||
return app(XenforoService::class)->getXfUser($this->user_id);
|
return app(XenforoService::class)->getXfUser($this->user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getActivitylogOptions(): LogOptions
|
||||||
|
{
|
||||||
|
return LogOptions::defaults()
|
||||||
|
->useLogName('author')
|
||||||
|
->logAll()
|
||||||
|
->logOnlyDirty()
|
||||||
|
->dontLogEmptyChanges()
|
||||||
|
->setDescriptionForEvent(fn(string $eventName) => "Author {$eventName}");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,6 +253,11 @@ class Entry extends Model
|
|||||||
return $converter->convert($this->description)->getContent();
|
return $converter->convert($this->description)->getContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTotalDownloadsAttribute(): int
|
||||||
|
{
|
||||||
|
return $this->files->sum('download_count') + $this->old_download_count;
|
||||||
|
}
|
||||||
|
|
||||||
public function parseStaffCredits(): ?array {
|
public function parseStaffCredits(): ?array {
|
||||||
return json_decode( $this->staff_credits ?? "", true );
|
return json_decode( $this->staff_credits ?? "", true );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
||||||
|
use Spatie\Activitylog\Models\Concerns\LogsActivity;
|
||||||
|
use Spatie\Activitylog\Support\LogOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -41,7 +43,7 @@ use League\CommonMark\GithubFlavoredMarkdownConverter;
|
|||||||
class EntryReview extends Model
|
class EntryReview extends Model
|
||||||
{
|
{
|
||||||
|
|
||||||
use HasXenforoUserId, SoftDeletes;
|
use HasXenforoUserId, SoftDeletes, LogsActivity;
|
||||||
|
|
||||||
protected $fillable = [ 'entry_id', 'title', 'rating', 'description', 'user_id' ];
|
protected $fillable = [ 'entry_id', 'title', 'rating', 'description', 'user_id' ];
|
||||||
|
|
||||||
@@ -60,4 +62,14 @@ class EntryReview extends Model
|
|||||||
return $converter->convert($this->description)->getContent();
|
return $converter->convert($this->description)->getContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getActivitylogOptions(): LogOptions
|
||||||
|
{
|
||||||
|
return LogOptions::defaults()
|
||||||
|
->useLogName('review')
|
||||||
|
->logAll()
|
||||||
|
->logOnlyDirty()
|
||||||
|
->dontLogEmptyChanges()
|
||||||
|
->setDescriptionForEvent(fn(string $eventName) => "Review {$eventName}");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ namespace App\Models;
|
|||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Spatie\Activitylog\Models\Concerns\LogsActivity;
|
||||||
|
use Spatie\Activitylog\Support\LogOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -31,6 +33,9 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||||||
*/
|
*/
|
||||||
class Game extends Model
|
class Game extends Model
|
||||||
{
|
{
|
||||||
|
|
||||||
|
use LogsActivity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
@@ -50,4 +55,14 @@ class Game extends Model
|
|||||||
{
|
{
|
||||||
return $this->hasMany(Entry::class);
|
return $this->hasMany(Entry::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getActivitylogOptions(): LogOptions
|
||||||
|
{
|
||||||
|
return LogOptions::defaults()
|
||||||
|
->useLogName('game')
|
||||||
|
->logAll()
|
||||||
|
->logOnlyDirty()
|
||||||
|
->dontLogEmptyChanges()
|
||||||
|
->setDescriptionForEvent(fn(string $eventName) => "Game {$eventName}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -23,4 +24,9 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
class Level extends Model
|
class Level extends Model
|
||||||
{
|
{
|
||||||
protected $fillable = ['name', 'slug'];
|
protected $fillable = ['name', 'slug'];
|
||||||
|
|
||||||
|
public function entries(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Entry::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -23,4 +25,9 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
class Modification extends Model
|
class Modification extends Model
|
||||||
{
|
{
|
||||||
protected $fillable = [ 'name', 'slug' ];
|
protected $fillable = [ 'name', 'slug' ];
|
||||||
|
|
||||||
|
public function entries(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Entry::class, 'entry_modifications');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
||||||
|
use Spatie\Activitylog\Models\Concerns\LogsActivity;
|
||||||
|
use Spatie\Activitylog\Support\LogOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -61,7 +63,7 @@ use League\CommonMark\GithubFlavoredMarkdownConverter;
|
|||||||
class News extends Model
|
class News extends Model
|
||||||
{
|
{
|
||||||
|
|
||||||
use SoftDeletes, HasGallery, HasXenforoUserId;
|
use SoftDeletes, HasGallery, HasXenforoUserId, LogsActivity;
|
||||||
|
|
||||||
protected $table = 'news';
|
protected $table = 'news';
|
||||||
|
|
||||||
@@ -127,4 +129,14 @@ class News extends Model
|
|||||||
return EntryHelpers::getYoutubeVideoId( $this->youtube_link );
|
return EntryHelpers::getYoutubeVideoId( $this->youtube_link );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getActivitylogOptions(): LogOptions
|
||||||
|
{
|
||||||
|
return LogOptions::defaults()
|
||||||
|
->useLogName('news')
|
||||||
|
->logAll()
|
||||||
|
->logOnlyDirty()
|
||||||
|
->dontLogEmptyChanges()
|
||||||
|
->setDescriptionForEvent(fn(string $eventName) => "News {$eventName}");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ class EntryPolicy
|
|||||||
return $user->_can('romhackplaza', 'canModerateEntries' );
|
return $user->_can('romhackplaza', 'canModerateEntries' );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function moderate(User $user, Entry $entry): bool
|
public function moderate(User $user, ?Entry $entry = null): bool
|
||||||
{
|
{
|
||||||
return $user->_can('romhackplaza', 'canModerateEntries' );
|
return $user->_can('romhackplaza', 'canModerateEntries' );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Helpers\EntryHelpers;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\EntryReview;
|
use App\Models\EntryReview;
|
||||||
use App\Models\News;
|
use App\Models\News;
|
||||||
@@ -65,7 +66,7 @@ class ActivityService
|
|||||||
'user_id' => $entry->user_id,
|
'user_id' => $entry->user_id,
|
||||||
'badge' => EntryCard::ENTRY_TYPES_BADGE[$entry->type],
|
'badge' => EntryCard::ENTRY_TYPES_BADGE[$entry->type],
|
||||||
'badge_class' => $entry->type,
|
'badge_class' => $entry->type,
|
||||||
'excerpt' => $entry->description ? \Str::limit(strip_tags($entry->description), 80) : null,
|
'excerpt' => $entry->description ? \Str::limit(EntryHelpers::stripMarkdown(strip_tags($entry->description)), 80) : null,
|
||||||
'meta' => $entry->getRealPlatform()?->name
|
'meta' => $entry->getRealPlatform()?->name
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -82,7 +83,7 @@ class ActivityService
|
|||||||
'user_id' => $news->user_id,
|
'user_id' => $news->user_id,
|
||||||
'badge' => 'News',
|
'badge' => 'News',
|
||||||
'badge_class' => 'news',
|
'badge_class' => 'news',
|
||||||
'excerpt' => $news->description ? \Str::limit(strip_tags($news->description), 80) : null,
|
'excerpt' => $news->description ? \Str::limit(EntryHelpers::stripMarkdown(strip_tags($news->description)), 80) : null,
|
||||||
'meta' => $news->category?->name
|
'meta' => $news->category?->name
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -99,7 +100,7 @@ class ActivityService
|
|||||||
'user_id' => $message->user_id,
|
'user_id' => $message->user_id,
|
||||||
'badge' => 'Post',
|
'badge' => 'Post',
|
||||||
'badge_class' => 'message',
|
'badge_class' => 'message',
|
||||||
'excerpt' => $message->message ? \Str::limit(strip_tags($message->message), 80) : null,
|
'excerpt' => $message->message ? \Str::limit(EntryHelpers::stripBbCode(strip_tags($message->message)), 80) : null,
|
||||||
'meta' => null
|
'meta' => null
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -117,7 +118,7 @@ class ActivityService
|
|||||||
'user_id' => $thread->user_id,
|
'user_id' => $thread->user_id,
|
||||||
'badge' => 'Thread',
|
'badge' => 'Thread',
|
||||||
'badge_class' => 'thread',
|
'badge_class' => 'thread',
|
||||||
'excerpt' => $thread->message ? \Str::limit(strip_tags($thread->message), 80) : null,
|
'excerpt' => $thread->message ? \Str::limit(EntryHelpers::stripBbCode(strip_tags($thread->message)), 80) : null,
|
||||||
'meta' => null
|
'meta' => null
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -152,7 +153,7 @@ class ActivityService
|
|||||||
'user_id' => $review->user_id,
|
'user_id' => $review->user_id,
|
||||||
'badge' => 'Review',
|
'badge' => 'Review',
|
||||||
'badge_class' => 'review',
|
'badge_class' => 'review',
|
||||||
'excerpt' => $review->description ? \Str::limit(strip_tags($review->description), 80) : null,
|
'excerpt' => $review->description ? \Str::limit(EntryHelpers::stripMarkdown(strip_tags($review->description)), 80) : null,
|
||||||
'meta' => $review->entry()->exists() ? ( $review->entry->complete_title ?? $review->entry->title ) : null,
|
'meta' => $review->entry()->exists() ? ( $review->entry->complete_title ?? $review->entry->title ) : null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -199,6 +200,9 @@ class ActivityService
|
|||||||
'post.user_id', 'post.message'
|
'post.user_id', 'post.message'
|
||||||
])
|
])
|
||||||
->get()
|
->get()
|
||||||
|
->reject(function($post){
|
||||||
|
return (int) $post->user_id === config('xenforo.bot_user_id');
|
||||||
|
})
|
||||||
->map($this->formatMessage(...))
|
->map($this->formatMessage(...))
|
||||||
->toArray();
|
->toArray();
|
||||||
});
|
});
|
||||||
@@ -213,6 +217,7 @@ class ActivityService
|
|||||||
->join('post', 'thread.first_post_id', '=', 'post.post_id')
|
->join('post', 'thread.first_post_id', '=', 'post.post_id')
|
||||||
->where('thread.discussion_state', 'visible')
|
->where('thread.discussion_state', 'visible')
|
||||||
->where('thread.discussion_type', '!=', 'redirect' )
|
->where('thread.discussion_type', '!=', 'redirect' )
|
||||||
|
->where('thread.node_id', '!=', config('xenforo.comments_node_id') )
|
||||||
->orderByDesc('thread.post_date')
|
->orderByDesc('thread.post_date')
|
||||||
->limit(self::ITEMS_PER_TYPE)
|
->limit(self::ITEMS_PER_TYPE)
|
||||||
->select([
|
->select([
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
|
use App\Models\News;
|
||||||
use Illuminate\Http\Client\ConnectionException;
|
use Illuminate\Http\Client\ConnectionException;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
@@ -24,7 +25,7 @@ class XenforoApiService {
|
|||||||
*/
|
*/
|
||||||
private function get(string $endpoint, ?int $customUserId = null ): mixed
|
private function get(string $endpoint, ?int $customUserId = null ): mixed
|
||||||
{
|
{
|
||||||
$response = Http::timeout(30)->withOptions(['verify' => false])->withHeaders([
|
$response = Http::timeout(8)->connectTimeout(4)->withOptions(['verify' => false])->withHeaders([
|
||||||
'XF-Api-Key' => $this->apiKey,
|
'XF-Api-Key' => $this->apiKey,
|
||||||
'XF-Api-User' => $customUserId ?? $this->superUserId,
|
'XF-Api-User' => $customUserId ?? $this->superUserId,
|
||||||
])->get("{$this->apiUrl}/{$endpoint}");
|
])->get("{$this->apiUrl}/{$endpoint}");
|
||||||
@@ -39,7 +40,7 @@ class XenforoApiService {
|
|||||||
|
|
||||||
private function post(string $endpoint, ?int $customUserId = null, array $data = [] ): mixed
|
private function post(string $endpoint, ?int $customUserId = null, array $data = [] ): mixed
|
||||||
{
|
{
|
||||||
$response = Http::timeout(30)->withOptions(['verify' => false])->withHeaders([
|
$response = Http::timeout(8)->connectTimeout(4)->withOptions(['verify' => false])->withHeaders([
|
||||||
'XF-Api-Key' => $this->apiKey,
|
'XF-Api-Key' => $this->apiKey,
|
||||||
'XF-Api-User' => $customUserId ?? $this->superUserId,
|
'XF-Api-User' => $customUserId ?? $this->superUserId,
|
||||||
])->post("{$this->apiUrl}/{$endpoint}", $data);
|
])->post("{$this->apiUrl}/{$endpoint}", $data);
|
||||||
@@ -54,7 +55,7 @@ class XenforoApiService {
|
|||||||
|
|
||||||
private function delete(string $endpoint, ?int $customUserId = null, array $data = [] ): mixed
|
private function delete(string $endpoint, ?int $customUserId = null, array $data = [] ): mixed
|
||||||
{
|
{
|
||||||
$response = Http::timeout(30)->withOptions(['verify' => false])->withHeaders([
|
$response = Http::timeout(8)->connectTimeout(4)->withOptions(['verify' => false])->withHeaders([
|
||||||
'XF-Api-Key' => $this->apiKey,
|
'XF-Api-Key' => $this->apiKey,
|
||||||
'XF-Api-User' => $customUserId ?? $this->superUserId,
|
'XF-Api-User' => $customUserId ?? $this->superUserId,
|
||||||
])->delete("{$this->apiUrl}/{$endpoint}", $data);
|
])->delete("{$this->apiUrl}/{$endpoint}", $data);
|
||||||
@@ -81,7 +82,7 @@ class XenforoApiService {
|
|||||||
public function markAllNotificationsRead(int $userId): void
|
public function markAllNotificationsRead(int $userId): void
|
||||||
{
|
{
|
||||||
Cache::forget("xf_alerts_{$userId}");
|
Cache::forget("xf_alerts_{$userId}");
|
||||||
$this->post("alerts/mark-all", $userId );
|
$this->post("alerts/mark-all", $userId, ['read' => true, 'viewed' => true] );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getConversations(int $userId): mixed
|
public function getConversations(int $userId): mixed
|
||||||
@@ -104,10 +105,10 @@ class XenforoApiService {
|
|||||||
if( !$entry->comments_thread_id || $entry->comments_thread_id <= 0 ){
|
if( !$entry->comments_thread_id || $entry->comments_thread_id <= 0 ){
|
||||||
$data = [
|
$data = [
|
||||||
'node_id' => config('xenforo.comments_node_id'),
|
'node_id' => config('xenforo.comments_node_id'),
|
||||||
'title' => $entry->complete_title,
|
'title' => $entry->complete_title ?? $entry->title,
|
||||||
'message' => $entry->description,
|
'message' => $entry->description,
|
||||||
'prefix_id' => config('xenforo.comments_prefixes')[$entry->type] ?? 1,
|
'prefix_id' => config('xenforo.comments_prefixes')[$entry->type ?? "news"] ?? 1,
|
||||||
'custom_fields' => [ 'entry_id' => $entry->id ],
|
'custom_fields' => $entry->type ? [ 'entry_id' => $entry->id ] : [ 'news_id' => $entry->id ],
|
||||||
'discussion_open' => true,
|
'discussion_open' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ class ErrorBlock extends Component
|
|||||||
'icon' => 'ban',
|
'icon' => 'ban',
|
||||||
'message' => '%s'
|
'message' => '%s'
|
||||||
],
|
],
|
||||||
|
'not-found' => [
|
||||||
|
'icon' => 'sticky-note-off',
|
||||||
|
'message' => "404: This page does not exist."
|
||||||
|
],
|
||||||
'page-not-allowed' => [
|
'page-not-allowed' => [
|
||||||
'icon' => 'shield-ban',
|
'icon' => 'shield-ban',
|
||||||
'message' => "You do not have permission to access this page.\nRequired permission: %s"
|
'message' => "You do not have permission to access this page.\nRequired permission: %s"
|
||||||
@@ -21,6 +25,10 @@ class ErrorBlock extends Component
|
|||||||
'user-state-not-valid' => [
|
'user-state-not-valid' => [
|
||||||
'icon' => 'shield-ban',
|
'icon' => 'shield-ban',
|
||||||
'message' => "You do not have permission to access this page.\nYour user profile is incomplete: %s\nGo back to the forum for more details."
|
'message' => "You do not have permission to access this page.\nYour user profile is incomplete: %s\nGo back to the forum for more details."
|
||||||
|
],
|
||||||
|
'server-error' => [
|
||||||
|
'icon' => 'bomb',
|
||||||
|
'message' => "500: Internal Server Error."
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ class SubmitEntryStatus extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( $this->isEdit && $this->entry->state === 'pending' && isset( $states['published'] ) ) {
|
||||||
|
unset( $states['published'] );
|
||||||
|
}
|
||||||
|
|
||||||
return $states;
|
return $states;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||||||
$middleware->encryptCookies(except: ['xf_session','xf_user','xf_csrf','xf_style_variation','activity_filters']);
|
$middleware->encryptCookies(except: ['xf_session','xf_user','xf_csrf','xf_style_variation','activity_filters']);
|
||||||
$middleware->alias([
|
$middleware->alias([
|
||||||
'xf.auth' => \App\Http\Middleware\CheckXenForoPermissions::class,
|
'xf.auth' => \App\Http\Middleware\CheckXenForoPermissions::class,
|
||||||
|
'submissions.maintenance' => \App\Http\Middleware\SubmissionsEnabled::class,
|
||||||
]);
|
]);
|
||||||
$middleware->redirectGuestsTo(function(Request $request): void {
|
$middleware->redirectGuestsTo(function(Request $request): void {
|
||||||
if( $request->is('manage*'))
|
if( $request->is('manage*'))
|
||||||
@@ -24,5 +25,28 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||||||
$middleware->append(\App\Http\Middleware\CheckXenForoUserState::class);
|
$middleware->append(\App\Http\Middleware\CheckXenForoUserState::class);
|
||||||
})
|
})
|
||||||
->withExceptions(function (Exceptions $exceptions): void {
|
->withExceptions(function (Exceptions $exceptions): void {
|
||||||
//
|
$exceptions->render(function(Throwable $e, $request) {
|
||||||
|
if (
|
||||||
|
$e instanceof \Illuminate\Validation\ValidationException ||
|
||||||
|
$e instanceof \Illuminate\Auth\AuthenticationException ||
|
||||||
|
$e instanceof \Illuminate\Auth\Access\AuthorizationException ||
|
||||||
|
$e instanceof \Symfony\Component\HttpKernel\Exception\HttpException
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$statusCode = method_exists($e, 'getStatusCode') ? $e->getStatusCode() : 500;
|
||||||
|
|
||||||
|
if( app()->environment('production') && $statusCode >= 500 && !$request->expectsJson() ){
|
||||||
|
$errorId = (string) Str::uuid();
|
||||||
|
|
||||||
|
\Illuminate\Support\Facades\Log::error($e->getMessage(), [
|
||||||
|
'error_id' => $errorId,
|
||||||
|
'exception' => $e,
|
||||||
|
'url' => $request->fullUrl(),
|
||||||
|
'user_id' => (optional($request->user())->user_id ?? 0),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return response()->view('errors.500', ['errorId' => $errorId], 500 );
|
||||||
|
}
|
||||||
|
});
|
||||||
})->create();
|
})->create();
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
return [
|
|
||||||
'bot_user_id' => 1,
|
|
||||||
|
|
||||||
'comments_node_id' => 4,
|
|
||||||
|
|
||||||
'comments_prefixes' => [
|
|
||||||
'translations' => 1,
|
|
||||||
'romhacks' => 2,
|
|
||||||
'homebrew' => 3,
|
|
||||||
'utilities' => 4,
|
|
||||||
'documents' => 5,
|
|
||||||
'lua-scripts' => 6,
|
|
||||||
'tutorials' => 7,
|
|
||||||
'news' => 8
|
|
||||||
],
|
|
||||||
];
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('entries', function (Blueprint $table) {
|
||||||
|
$table->unsignedBigInteger('old_download_count')->default(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('entries', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('old_download_count');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -9,7 +9,8 @@ body {
|
|||||||
background-color: var(--bg);
|
background-color: var(--bg);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100vh;
|
height: 100dvh;
|
||||||
|
min-height: 100dvh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
display: flex;
|
display: flex;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
transition: transform 0.2s, border-color 0.2s;
|
transition: transform 0.2s, border-color 0.2s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -72,6 +73,7 @@
|
|||||||
.entry-card-info {
|
.entry-card-info {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
@@ -82,13 +84,16 @@
|
|||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
word-wrap: break-word;
|
overflow-wrap: anywhere;
|
||||||
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry-card-author {
|
.entry-card-author {
|
||||||
color: var(--rhpz-orange);
|
color: var(--rhpz-orange);
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry-card-meta {
|
.entry-card-meta {
|
||||||
@@ -148,6 +153,9 @@
|
|||||||
|
|
||||||
.entry-card-meta {
|
.entry-card-meta {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,45 @@
|
|||||||
background-color: rgba(129, 199, 132, 0.1);
|
background-color: rgba(129, 199, 132, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.back-to-top {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.back-to-top {
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
position: fixed;
|
||||||
|
right: 14px;
|
||||||
|
bottom: 14px;
|
||||||
|
z-index: 1000;
|
||||||
|
padding: 10px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background-color: var(--bg2);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
color: var(--text);
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transform: translateY(10px);
|
||||||
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-to-top.visible {
|
||||||
|
display: inline-flex;
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-to-top span {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* BLOCK */
|
/* BLOCK */
|
||||||
|
|
||||||
.block {
|
.block {
|
||||||
@@ -94,21 +133,36 @@
|
|||||||
color: var(--text3);
|
color: var(--text3);
|
||||||
border-color: var(--rhpz-orange);
|
border-color: var(--rhpz-orange);
|
||||||
}
|
}
|
||||||
.badge.blue, .badge.translations {
|
.badge.blue, .badge.romhacks {
|
||||||
background-color: var(--info);
|
background-color: var(--info);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
border-color: var(--info);
|
border-color: var(--info);
|
||||||
}
|
}
|
||||||
.badge.green, .badge.romhacks {
|
.badge.green, .badge.translations {
|
||||||
background-color: var(--success2);
|
background-color: var(--success2);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
border-color: var(--success2);
|
border-color: var(--success2);
|
||||||
}
|
}
|
||||||
|
.badge.red, .badge.homebrew {
|
||||||
|
background-color: #bf2323;
|
||||||
|
color: var(--text);
|
||||||
|
border-color: #bf2323;
|
||||||
|
}
|
||||||
.badge.yellow, .badge.utilities {
|
.badge.yellow, .badge.utilities {
|
||||||
background-color: #fdeb0f;
|
background-color: #fdeb0f;
|
||||||
color: #000;
|
color: #000;
|
||||||
border-color: #fdeb0f;
|
border-color: #fdeb0f;
|
||||||
}
|
}
|
||||||
|
.badge.purple, .badge.documents{
|
||||||
|
background-color: #8b23bf;
|
||||||
|
color: var(--text);
|
||||||
|
border-color: #8b23bf;
|
||||||
|
}
|
||||||
|
.badge.brown, .badge.lua-scripts {
|
||||||
|
background-color: #b4825f;
|
||||||
|
color: var(--text);
|
||||||
|
border-color: #b4825f;
|
||||||
|
}
|
||||||
|
|
||||||
.topbar-badge {
|
.topbar-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -227,8 +227,10 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
min-width: 36px;
|
min-width: 36px;
|
||||||
@@ -255,18 +257,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
.database-layout {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.database-wrapper {
|
.database-wrapper {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.database-filters {
|
.database-filters {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2,1fr);
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.database-results {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.database-filter-group:last-child {
|
.database-filter-group:last-child {
|
||||||
@@ -286,13 +290,32 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filter-bar {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar .filter-bar-search {
|
||||||
|
max-width: none;
|
||||||
|
flex: initial;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar .btn {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.database-wrapper {
|
.database-wrapper {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.database-filters {
|
.database-filters {
|
||||||
width: 100%;
|
width: min(100%, 420px);
|
||||||
|
max-width: 420px;
|
||||||
|
margin: 0 auto;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
order: -1;
|
order: -1;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
@@ -303,7 +326,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.grid-entries {
|
.grid-entries {
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,6 +334,20 @@
|
|||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.database-search {
|
.database-search {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar .filter-bar-search {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar .btn {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.database-filters {
|
.database-filters {
|
||||||
@@ -318,7 +355,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.grid-entries {
|
.grid-entries {
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -393,6 +393,37 @@
|
|||||||
min-width: 20px;
|
min-width: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gallery-mobile-controls {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.gallery-mobile-controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-move-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background-color: var(--bg2);
|
||||||
|
color: var(--text);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-move-btn:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
.authors-list {
|
.authors-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
@@ -510,13 +541,30 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.upload-item-actions {
|
.upload-item {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-item-info {
|
||||||
|
width: 100%;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-item-actions {
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 8px;
|
||||||
|
flex-basis: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-item-actions .btn {
|
.upload-item-actions .btn {
|
||||||
width: 100%;
|
width: auto;
|
||||||
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.file-state-icon { width: 18px; height: 18px; }
|
.file-state-icon { width: 18px; height: 18px; }
|
||||||
@@ -673,10 +721,18 @@
|
|||||||
.grid-hashes {
|
.grid-hashes {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
.grid-credits {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.grid-first {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.hash-first {
|
.hash-first {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.author-search-selected {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
@@ -706,4 +762,20 @@
|
|||||||
.form-error-text {
|
.form-error-text {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-type-of-checkboxes {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
label {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
transform: scale(1.5);
|
||||||
|
margin-right: 1.33rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,38 @@
|
|||||||
|
|
||||||
.grid-entries {
|
.grid-entries {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(6,1fr);
|
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1400px) {
|
||||||
|
.grid-entries {
|
||||||
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.grid-entries {
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.grid-entries {
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.grid-entries {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 420px) {
|
||||||
|
.grid-entries {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -223,7 +223,7 @@
|
|||||||
.activity-tl-card-description {
|
.activity-tl-card-description {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
color: var(--text2);
|
color: var(--text2);
|
||||||
white-space: nowrap;
|
word-break: break-word;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
@@ -265,6 +265,8 @@
|
|||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.activity-tl-header { flex-direction: column; align-items: flex-start; }
|
.activity-tl-header { flex-direction: column; align-items: flex-start; }
|
||||||
.activity-tl-thumb { display: none; }
|
.activity-tl-thumb { display: none; }
|
||||||
|
.activity-tl-left { display: none; }
|
||||||
|
.activity-tl-card-description { display: none; }
|
||||||
.activity-day-sep { padding-left: 44px; }
|
.activity-day-sep { padding-left: 44px; }
|
||||||
.activity-tl-left { width: 44px; }
|
.activity-tl-left { width: 44px; }
|
||||||
|
|
||||||
@@ -279,7 +281,7 @@
|
|||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.activity-timeline {
|
.activity-timeline {
|
||||||
padding-left: 50px;
|
padding-left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-tl-left {
|
.activity-tl-left {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#topbar {
|
#topbar {
|
||||||
|
|||||||
@@ -101,6 +101,30 @@
|
|||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
|
word-break: break-word;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 0 14px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin: 0 0 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry-gallery {
|
.entry-gallery {
|
||||||
@@ -220,6 +244,9 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: var(--bg4);
|
background-color: var(--bg4);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
transition: transform 0.3s ease;
|
transition: transform 0.3s ease;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.menu-header {
|
.menu-header {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
@@ -44,9 +45,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-navigation {
|
.menu-navigation {
|
||||||
flex-grow: 1;
|
flex: 1 1 auto;
|
||||||
|
min-height: 0;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
|
||||||
.menu-group {
|
.menu-group {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
@@ -96,7 +100,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-user {
|
.menu-user {
|
||||||
padding: 15px 20px;
|
flex-shrink: 0;
|
||||||
|
margin-top: auto;
|
||||||
|
padding: 15px 20px calc(15px + env(safe-area-inset-bottom)) 20px;
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -131,6 +137,7 @@
|
|||||||
&.username {
|
&.username {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
text-decoration: none !important;
|
||||||
}
|
}
|
||||||
&.user_role {
|
&.user_role {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 60px;
|
top: 60px;
|
||||||
height: calc(100vh - 60px);
|
height: calc(100dvh - 60px);
|
||||||
|
max-height: calc(100dvh - 60px);
|
||||||
|
min-height: 0;
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
transition: transform 0.3s ease-in-out;
|
transition: transform 0.3s ease-in-out;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
|
|||||||
@@ -131,15 +131,38 @@ export function GalleryManager() {
|
|||||||
this.dragSrcI = index;
|
this.dragSrcI = index;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
moveImageUp(index){
|
||||||
|
if( index <= 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const moved = this.images.splice(index, 1)[0];
|
||||||
|
this.images.splice(index - 1, 0, moved);
|
||||||
|
},
|
||||||
|
|
||||||
|
moveImageDown(index){
|
||||||
|
if( index >= this.images.length - 1 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const moved = this.images.splice(index, 1)[0];
|
||||||
|
this.images.splice(index + 1, 0, moved);
|
||||||
|
},
|
||||||
|
|
||||||
|
reorderImages(from, to){
|
||||||
|
if( from === null || to === null || from === to )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const moved = this.images.splice(from, 1)[0];
|
||||||
|
this.images.splice(to, 0, moved);
|
||||||
|
this.dragSrcI = to;
|
||||||
|
},
|
||||||
|
|
||||||
dragOver(e, index){
|
dragOver(e, index){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if( this.dragSrcI === null || this.dragSrcI === index )
|
if( this.dragSrcI === null || this.dragSrcI === index )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const moved = this.images.splice(this.dragSrcI, 1)[0];
|
this.reorderImages(this.dragSrcI, index);
|
||||||
this.images.splice(index, 0, moved);
|
|
||||||
this.dragSrcI = index;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
dragEnd(){
|
dragEnd(){
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import notifications from "./notifications.js";
|
|||||||
import conversations from "./conversations.js";
|
import conversations from "./conversations.js";
|
||||||
import settings from "./settings.js";
|
import settings from "./settings.js";
|
||||||
import { initMobileMenu } from "./mobile-menu.js";
|
import { initMobileMenu } from "./mobile-menu.js";
|
||||||
|
import { initMobileBackToTop } from "./mobile-back-to-top.js"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get config defined in meta.blade.php
|
* Get config defined in meta.blade.php
|
||||||
@@ -47,3 +48,4 @@ Alpine.store('settings', settings() );
|
|||||||
|
|
||||||
// Mobile Menu
|
// Mobile Menu
|
||||||
document.addEventListener('DOMContentLoaded', initMobileMenu);
|
document.addEventListener('DOMContentLoaded', initMobileMenu);
|
||||||
|
document.addEventListener( 'DOMContentLoaded', initMobileBackToTop );
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ export default function hovercard(){
|
|||||||
Alpine.nextTick(() => {
|
Alpine.nextTick(() => {
|
||||||
const card = document.querySelector('.hovercard');
|
const card = document.querySelector('.hovercard');
|
||||||
if (card) window.refreshIcons(card);
|
if (card) window.refreshIcons(card);
|
||||||
|
this.updatePosition(this.anchorEl);
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch( error ){
|
} catch( error ){
|
||||||
@@ -89,13 +90,26 @@ export default function hovercard(){
|
|||||||
const RECT = anchorEl.getBoundingClientRect();
|
const RECT = anchorEl.getBoundingClientRect();
|
||||||
const SCROLL_X = window.scrollX;
|
const SCROLL_X = window.scrollX;
|
||||||
const SCROLL_Y = window.scrollY;
|
const SCROLL_Y = window.scrollY;
|
||||||
|
const VIEWPORT_WIDTH = window.innerWidth;
|
||||||
|
const VIEWPORT_HEIGHT = window.innerHeight;
|
||||||
|
|
||||||
let x = RECT.left + SCROLL_X;
|
const CARD = document.querySelector('.hovercard');
|
||||||
|
const WIDTH = CARD?.offsetWidth || 280;
|
||||||
|
const HEIGHT = CARD?.offsetHeight || 320;
|
||||||
|
|
||||||
|
let x = RECT.right + SCROLL_X + 8;
|
||||||
let y = RECT.bottom + SCROLL_Y + 8;
|
let y = RECT.bottom + SCROLL_Y + 8;
|
||||||
|
|
||||||
const WIDTH = 280;
|
if( x + WIDTH > VIEWPORT_WIDTH - 8 && RECT.left + SCROLL_X - WIDTH - 8 >= 8 ){
|
||||||
if( x + WIDTH > window.innerWidth ){
|
x = RECT.left + SCROLL_X - WIDTH - 8;
|
||||||
x = window.innerWidth - WIDTH - 16;
|
} else {
|
||||||
|
x = Math.max(8, Math.min(x, VIEWPORT_WIDTH - WIDTH - 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
if( y + HEIGHT > VIEWPORT_HEIGHT + SCROLL_Y - 8 && RECT.top + SCROLL_Y - HEIGHT - 8 >= 8 ){
|
||||||
|
y = RECT.top + SCROLL_Y - HEIGHT - 8;
|
||||||
|
} else {
|
||||||
|
y = Math.max(8, Math.min(y, VIEWPORT_HEIGHT + SCROLL_Y - HEIGHT - 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.x = x;
|
this.x = x;
|
||||||
|
|||||||
18
resources/js/mobile-back-to-top.js
Normal file
18
resources/js/mobile-back-to-top.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export function initMobileBackToTop(){
|
||||||
|
const backToTopButton = document.querySelector('.back-to-top');
|
||||||
|
const content = document.getElementById('content');
|
||||||
|
|
||||||
|
if (!backToTopButton || !content) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleBackToTop = () => {
|
||||||
|
const shouldShow = window.innerWidth <= 768 && content.scrollTop > 320;
|
||||||
|
|
||||||
|
backToTopButton.classList.toggle('visible', shouldShow);
|
||||||
|
};
|
||||||
|
|
||||||
|
toggleBackToTop();
|
||||||
|
content.addEventListener('scroll', toggleBackToTop, { passive: true });
|
||||||
|
window.addEventListener('resize', toggleBackToTop, { passive: true });
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="margin-bottom:10px">
|
<div style="margin-bottom:10px">
|
||||||
<span class="badge {{ $entry->type }}">{{ \App\View\Components\EntryCard::ENTRY_TYPES_BADGE[$entry->type] ?? $entry->type }}</span>
|
<span class="badge {{ $entry->type }}">{{ \App\View\Components\EntryCard::ENTRY_TYPES_BADGE[$entry->type] ?? $entry->type }}</span>
|
||||||
@if( section_must_be('romhacks', $entry->type ) )
|
@if( section_must_be(['romhacks', 'lua-scripts'], $entry->type ) )
|
||||||
@foreach( $entry->modifications as $modif )
|
@foreach( $entry->modifications as $modif )
|
||||||
<span class="badge orange">{{ $modif->name }}</span>
|
<span class="badge orange">{{ $modif->name }}</span>
|
||||||
@endforeach
|
@endforeach
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
@foreach( $entry->languages as $lang )
|
@foreach( $entry->languages as $lang )
|
||||||
<span class="badge orange">{{ $lang->name }}</span>
|
<span class="badge orange">{{ $lang->name }}</span>
|
||||||
@endforeach
|
@endforeach
|
||||||
@elseif( section_must_be( 'utilities', $entry->type ) )
|
@elseif( section_must_be( ['utilities', 'documents'], $entry->type ) )
|
||||||
@foreach( $entry->categories as $category )
|
@foreach( $entry->categories as $category )
|
||||||
<span class="badge orange">{{ $category->name }}</span>
|
<span class="badge orange">{{ $category->name }}</span>
|
||||||
@endforeach
|
@endforeach
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="entry-card-meta">
|
<div class="entry-card-meta">
|
||||||
<span><i data-lucide="download" size="12"></i> x</span>
|
<span><i data-lucide="download" size="12"></i> {{ $entry->total_downloads ?? 0 }}</span>
|
||||||
<span>Added: {{ $entry->created_at->format('y-m-d') }}</span>
|
<span>Added: {{ $entry->created_at->format('y-m-d') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,20 +2,28 @@
|
|||||||
<x-form-field-title name="Screenshots" helper="At least 1 Screenshot required, Maximum 20." required="{{ $required ? 'true' : 'false' }}" />
|
<x-form-field-title name="Screenshots" helper="At least 1 Screenshot required, Maximum 20." required="{{ $required ? 'true' : 'false' }}" />
|
||||||
<div class="form-group main-image-grid">
|
<div class="form-group main-image-grid">
|
||||||
<div class="form-upload" style="flex:1;" :class="{ 'disabled': isFull }">
|
<div class="form-upload" style="flex:1;" :class="{ 'disabled': isFull }">
|
||||||
<input type="file" id="gallery-field" accept="image/png, image/jpeg, image/webp" multiple :disabled="isFull" @change="handleSubmitFiles($event)">
|
<input type="file" id="gallery-field" accept="image/png, image/jpeg, image/webp, image/gif" multiple :disabled="isFull" @change="handleSubmitFiles($event)">
|
||||||
<div class="form-upload-placeholder level">
|
<div class="form-upload-placeholder level">
|
||||||
<i data-lucide="file-archive" size="36" style="margin-bottom:15px;color:var(--text2)"></i>
|
<i data-lucide="file-archive" size="36" style="margin-bottom:15px;color:var(--text2)"></i>
|
||||||
<div style="font-size: 1.1rem;color:var(--text);margin-bottom:5px;">
|
<div style="font-size: 1.1rem;color:var(--text);margin-bottom:5px;">
|
||||||
Click or drag'n drop files here.
|
Click or drag'n drop files here.
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size:0.85rem;color:var(--text2);">
|
<div style="font-size:0.85rem;color:var(--text2);">
|
||||||
Accepted: PNG, JPG or WebP
|
Accepted: PNG, JPG, GIF or WebP
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-gallery form-group level" style="flex:4;">
|
<div class="form-gallery form-group level" style="flex:4;">
|
||||||
<template x-for="(image,i) in images" :key="image.key">
|
<template x-for="(image,i) in images" :key="image.key">
|
||||||
<div class="gallery-item" :class="{ 'gallery-item--dragging': dragSrcI === i }" draggable="true" @dragstart="dragStart(i)" @dragover="dragOver($event, i)" @dragend="dragEnd()">
|
<div
|
||||||
|
class="gallery-item"
|
||||||
|
:class="{ 'gallery-item--dragging': dragSrcI === i }"
|
||||||
|
draggable="true"
|
||||||
|
:data-gallery-index="i"
|
||||||
|
@dragstart="dragStart(i)"
|
||||||
|
@dragover="dragOver($event, i)"
|
||||||
|
@dragend="dragEnd()"
|
||||||
|
>
|
||||||
<div class="form-image-preview-wrap">
|
<div class="form-image-preview-wrap">
|
||||||
<div class="gallery-drag-handle" title="Drag to reorder">
|
<div class="gallery-drag-handle" title="Drag to reorder">
|
||||||
<i data-lucide="grip-vertical" size="14"></i>
|
<i data-lucide="grip-vertical" size="14"></i>
|
||||||
@@ -28,6 +36,14 @@
|
|||||||
X
|
X
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="gallery-mobile-controls" aria-label="Reorder screenshot">
|
||||||
|
<button type="button" class="gallery-move-btn" @click="moveImageUp(i)" :disabled="i === 0" title="Move up">
|
||||||
|
<i data-lucide="chevron-up" size="14"></i>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="gallery-move-btn" @click="moveImageDown(i)" :disabled="i === images.length - 1" title="Move down">
|
||||||
|
<i data-lucide="chevron-down" size="14"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
<x-form-field-title name="Main image" helper="This will show up on the index and on top of the entry. A screenshot or custom cover is prefered else all entries of same game will look the same." required="{{ $required ? 'true' : 'false' }}" />
|
<x-form-field-title name="Main image" helper="This will show up on the index and on top of the entry. A screenshot or custom cover is prefered else all entries of same game will look the same." required="{{ $required ? 'true' : 'false' }}" />
|
||||||
<div class="form-group main-image-grid">
|
<div class="form-group main-image-grid">
|
||||||
<div class="form-upload" style="flex:4;">
|
<div class="form-upload" style="flex:4;">
|
||||||
<input type="file" id="main-image-field" accept="image/png, image/jpeg, image/webp" @change="handleSubmitFile($event)">
|
<input type="file" id="main-image-field" accept="image/png, image/jpeg, image/webp, image/gif" @change="handleSubmitFile($event)">
|
||||||
<div class="form-upload-placeholder level">
|
<div class="form-upload-placeholder level">
|
||||||
<i data-lucide="file-archive" size="36" style="margin-bottom:15px;color:var(--text2)"></i>
|
<i data-lucide="file-archive" size="36" style="margin-bottom:15px;color:var(--text2)"></i>
|
||||||
<div style="font-size: 1.1rem;color:var(--text);margin-bottom:5px;">
|
<div style="font-size: 1.1rem;color:var(--text);margin-bottom:5px;">
|
||||||
Click or drag'n drop files here.
|
Click or drag'n drop files here.
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size:0.85rem;color:var(--text2);">
|
<div style="font-size:0.85rem;color:var(--text2);">
|
||||||
Accepted: PNG, JPG or WebP
|
Accepted: PNG, JPG, GIF or WebP
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="main-image" x-model="serverFilePath">
|
<input type="hidden" name="main-image" x-model="serverFilePath">
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
<x-form-field-title name="Staff/Credits" />
|
<x-form-field-title name="Staff/Credits" />
|
||||||
<template x-if="credits.length > 0">
|
<template x-if="credits.length > 0">
|
||||||
<div class="form-group grid-credits">
|
<div class="form-group grid-credits">
|
||||||
<div><x-form-field-title name="Name" /></div>
|
<div class="grid-first"><x-form-field-title name="Name" /></div>
|
||||||
<div><x-form-field-title name="Description" /></div>
|
<div class="grid-first"><x-form-field-title name="Description" /></div>
|
||||||
<div><x-form-field-title name="Actions" /></div>
|
<div class="grid-first"><x-form-field-title name="Actions" /></div>
|
||||||
<template x-for="(credit,i) in credits" :key="i">
|
<template x-for="(credit,i) in credits" :key="i">
|
||||||
<div style="display:contents">
|
<div style="display:contents">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="comment-body">
|
<div class="comment-body">
|
||||||
{!! $comment['message'] !!}
|
{!! \App\Helpers\EntryHelpers::stripBbCode($comment['message']) !!}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -122,6 +122,9 @@
|
|||||||
<x-entry-meta-item label="Release Date" value="{{ $entry->release_date->format('Y-m-d') }}"
|
<x-entry-meta-item label="Release Date" value="{{ $entry->release_date->format('Y-m-d') }}"
|
||||||
route="none"/>
|
route="none"/>
|
||||||
@endif
|
@endif
|
||||||
|
@if( $entry->total_downloads )
|
||||||
|
<x-entry-meta-item label="Downloads" value="{{ $entry->total_downloads }}" route="none" />
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="hack-actions" style="display:flex;gap:10px;">
|
<div class="hack-actions" style="display:flex;gap:10px;">
|
||||||
@if($entry->state === 'pending')
|
@if($entry->state === 'pending')
|
||||||
|
|||||||
6
resources/views/errors/403.blade.php
Normal file
6
resources/views/errors/403.blade.php
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<x-error-block error-type="page-not-allowed" message="Unknown" />
|
||||||
|
<a class="btn" href="javascript:history.go(-1)">Go back to the previous page</a>
|
||||||
|
@endsection
|
||||||
6
resources/views/errors/404.blade.php
Normal file
6
resources/views/errors/404.blade.php
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<x-error-block error-type="not-found" />
|
||||||
|
<a class="btn" href="javascript:history.go(-1)">Go back to the previous page</a>
|
||||||
|
@endsection
|
||||||
6
resources/views/errors/4xx.blade.php
Normal file
6
resources/views/errors/4xx.blade.php
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<x-error-block error-type="custom" message="{{ $exception->getMessage() }}" />
|
||||||
|
<a class="btn" href="javascript:history.go(-1)">Go back to the previous page</a>
|
||||||
|
@endsection
|
||||||
305
resources/views/errors/500.blade.php
Normal file
305
resources/views/errors/500.blade.php
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>500 - Internal server error - Romhack Plaza</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<style>
|
||||||
|
|
||||||
|
:root {
|
||||||
|
|
||||||
|
/* RHPZ color */
|
||||||
|
--rhpz-orange: #ff7300;
|
||||||
|
--rhpz-orange-hover: #e56700;
|
||||||
|
|
||||||
|
/* Background colors */
|
||||||
|
--bg: #1e1e1e;
|
||||||
|
--bg2: #252526;
|
||||||
|
--bg3: #2d2d30;
|
||||||
|
--bg4: #3e3e42;
|
||||||
|
|
||||||
|
/* Text */
|
||||||
|
--text: #f1f1f1;
|
||||||
|
--text2: #a1a1aa;
|
||||||
|
--text3: #111111;
|
||||||
|
|
||||||
|
/* Elements */
|
||||||
|
--border: #3f3f46;
|
||||||
|
--error: #e57373;
|
||||||
|
--info: #1976d2;
|
||||||
|
--success: #81c784;
|
||||||
|
--success2: #388e3c;
|
||||||
|
|
||||||
|
/* Typo */
|
||||||
|
--typography: 'Segoe UI', 'San Francisco', 'Helvetica Neue', sans-serif;
|
||||||
|
|
||||||
|
/* Menu settings */
|
||||||
|
--menu-size: 260px;
|
||||||
|
--menu-user-avatar-bg: #555;
|
||||||
|
|
||||||
|
/* Gap */
|
||||||
|
--gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--typography);
|
||||||
|
background-color: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--rhpz-orange);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--rhpz-orange-hover);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
list-style-type: square;
|
||||||
|
}
|
||||||
|
|
||||||
|
[x-cloak] {display: none !important;}
|
||||||
|
|
||||||
|
|
||||||
|
/* BUTTONS */
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
background-color: var(--bg3);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
transition: all 0.1s;
|
||||||
|
}
|
||||||
|
.btn:hover {
|
||||||
|
background-color: var(--bg4);
|
||||||
|
border-color: var(--menu-user-avatar-bg);
|
||||||
|
}
|
||||||
|
.btn.primary {
|
||||||
|
background-color: var(--rhpz-orange);
|
||||||
|
color: var(--text3);
|
||||||
|
border-color: var(--rhpz-orange);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.btn.primary:hover {
|
||||||
|
background-color: var(--rhpz-orange-hover);
|
||||||
|
border-color: var(--rhpz-orange-hover);
|
||||||
|
}
|
||||||
|
.btn.danger {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--error);
|
||||||
|
border-color: var(--error);
|
||||||
|
}
|
||||||
|
.btn.danger:hover {
|
||||||
|
background-color: rgba(229, 115, 115, 0.1);
|
||||||
|
}
|
||||||
|
.btn.success {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--success);
|
||||||
|
border-color: var(--success);
|
||||||
|
}
|
||||||
|
.btn.success:hover {
|
||||||
|
background-color: rgba(129, 199, 132, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BLOCK */
|
||||||
|
|
||||||
|
.block {
|
||||||
|
background-color: var(--bg2);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.block.featured {
|
||||||
|
border-left: 3px solid var(--rhpz-orange);
|
||||||
|
}
|
||||||
|
.block-header {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: var(--text);
|
||||||
|
margin-bottom: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
.block-success {
|
||||||
|
background-color: var(--success);
|
||||||
|
border: 1px solid var(--success);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.block-error {
|
||||||
|
background-color: var(--error);
|
||||||
|
border: 1px solid var(--error);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PAGE */
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
font-weight: 300;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: var(--text);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TEXTS */
|
||||||
|
|
||||||
|
.whisper {
|
||||||
|
color: var(--text2);
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-title {
|
||||||
|
color: var(--text);
|
||||||
|
margin: 30px 0 15px 0;
|
||||||
|
border-left: 3px solid var(--rhpz-orange);
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote {
|
||||||
|
background-color: var(--bg);
|
||||||
|
border-left: 4px solid #1976d2;
|
||||||
|
padding: 15px;
|
||||||
|
margin-top: 30px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.btn {
|
||||||
|
padding: 7px 12px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-header {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-title {
|
||||||
|
margin: 20px 0 12px 0;
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote {
|
||||||
|
padding: 12px;
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whisper {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.btn {
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
gap: 4px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.primary, .btn.danger, .btn.success {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-header {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
padding: 2px 6px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topbar-badge {
|
||||||
|
min-width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
padding: 0 3px;
|
||||||
|
font-size: 0.6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app" style="display:flex;flex-direction: column;align-items: center;justify-content: center;">
|
||||||
|
<main id="main-wrapper">
|
||||||
|
<main id="content">
|
||||||
|
<div class="block">
|
||||||
|
<div class="page-title">500 - Internal server error</div>
|
||||||
|
<p>To resolve the issue quickly, please report it on Gitea, Discord, or the forum with the following information.</p>
|
||||||
|
<ul>
|
||||||
|
<li>Your error code: {{ $errorId ?? "" }}</li>
|
||||||
|
<li>What you were doing before the error occurred.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href="https://code.romhackplaza.org/RHPZAdmin/RomhackPlaza/issues" class="btn primary">Report the problem on Gitea</a>
|
||||||
|
<a href="https://community.romhackplaza.org/forums/bug-reports.6/" class="btn primary">Report the problem on the forum</a>
|
||||||
|
<a href="https://community.romhackplaza.org/misc/contact/" class="btn">Report the problem with the contact form</a>
|
||||||
|
<a href="https://discord.gg/5CKzeWmZZU" class="btn">Report the problem on Discord</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -24,12 +24,20 @@
|
|||||||
@if(session('error'))
|
@if(session('error'))
|
||||||
<x-error-block error-type="custom" :message="session('error')" />
|
<x-error-block error-type="custom" :message="session('error')" />
|
||||||
@endif
|
@endif
|
||||||
|
@if(isset($errors) && $errors->has('maintenance'))
|
||||||
|
<x-error-block error-type="custom" :message="$errors->get('maintenance')[0]" />
|
||||||
|
@endif
|
||||||
@yield('content')
|
@yield('content')
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<button type="button" class="back-to-top" aria-label="Back to top" onclick="document.getElementById('content')?.scrollTo({ top: 0, behavior: 'smooth' });">
|
||||||
|
<i data-lucide="arrow-up"></i>
|
||||||
|
<span>Top</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
@include('components.hovercard')
|
@include('components.hovercard')
|
||||||
@livewireScripts
|
@livewireScripts
|
||||||
@stack('scripts')
|
@stack('scripts')
|
||||||
|
|||||||
@@ -38,7 +38,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="menu-user-info">
|
<div class="menu-user-info">
|
||||||
<span class="username">
|
<span class="username">
|
||||||
{{ $VISITOR->username ?? "Guest" }}
|
@if( $VISITOR->guest() )
|
||||||
|
Guest
|
||||||
|
@else
|
||||||
|
<x-xf-username-link :user-id="$VISITOR->user_id" />
|
||||||
|
@endif
|
||||||
</span>
|
</span>
|
||||||
<span class="user_role">
|
<span class="user_role">
|
||||||
<a href="{{ $VISITOR->guest() ? xfRoute('login') : xfRoute('logout') . '?t=' . xfCsrfToken() }}">
|
<a href="{{ $VISITOR->guest() ? xfRoute('login') : xfRoute('logout') . '?t=' . xfCsrfToken() }}">
|
||||||
|
|||||||
@@ -27,10 +27,12 @@
|
|||||||
|
|
||||||
<div class="modcp-nav-group">
|
<div class="modcp-nav-group">
|
||||||
<span class="modcp-nav-label">Content</span>
|
<span class="modcp-nav-label">Content</span>
|
||||||
|
@can('moderate','\App\Models\Entry')
|
||||||
<a href="{{ route('modcp.locked') }}" class="modcp-nav-item" {{ request()->routeIs('modcp.locked') ? 'active' : '' }}>
|
<a href="{{ route('modcp.locked') }}" class="modcp-nav-item" {{ request()->routeIs('modcp.locked') ? 'active' : '' }}>
|
||||||
<i data-lucide="lock" size="15"></i>
|
<i data-lucide="lock" size="15"></i>
|
||||||
Locked entries
|
Locked entries
|
||||||
</a>
|
</a>
|
||||||
|
@endcan
|
||||||
@can('is-admin')
|
@can('is-admin')
|
||||||
<a href="{{ route('modcp.draft') }}" class="modcp-nav-item" {{ request()->routeIs('modcp.draft') ? 'active' : '' }}>
|
<a href="{{ route('modcp.draft') }}" class="modcp-nav-item" {{ request()->routeIs('modcp.draft') ? 'active' : '' }}>
|
||||||
<i data-lucide="scissors" size="15"></i>
|
<i data-lucide="scissors" size="15"></i>
|
||||||
@@ -74,6 +76,14 @@
|
|||||||
<i data-lucide="box" size="15"></i>
|
<i data-lucide="box" size="15"></i>
|
||||||
Genres
|
Genres
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{{ route('modcp.levels.index') }}" class="modcp-nav-item" {{ request()->routeIs('modcp.levels.*') ? 'active' : '' }}">
|
||||||
|
<i data-lucide="weight" size="15"></i>
|
||||||
|
Levels
|
||||||
|
</a>
|
||||||
|
<a href="{{ route('modcp.modifications.index') }}" class="modcp-nav-item" {{ request()->routeIs('modcp.modifications.*') ? 'active' : '' }}">
|
||||||
|
<i data-lucide="pencil-ruler" size="15"></i>
|
||||||
|
Modifications
|
||||||
|
</a>
|
||||||
@endcan
|
@endcan
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -203,7 +203,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="log-pagination">
|
<div class="log-pagination">
|
||||||
{{ $logs->links() }}
|
{{ $logs->links('modcp.pagination') }}
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
|||||||
299
resources/views/maintenance.blade.php
Normal file
299
resources/views/maintenance.blade.php
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>500 - Internal server error - Romhack Plaza</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<style>
|
||||||
|
|
||||||
|
:root {
|
||||||
|
|
||||||
|
/* RHPZ color */
|
||||||
|
--rhpz-orange: #ff7300;
|
||||||
|
--rhpz-orange-hover: #e56700;
|
||||||
|
|
||||||
|
/* Background colors */
|
||||||
|
--bg: #1e1e1e;
|
||||||
|
--bg2: #252526;
|
||||||
|
--bg3: #2d2d30;
|
||||||
|
--bg4: #3e3e42;
|
||||||
|
|
||||||
|
/* Text */
|
||||||
|
--text: #f1f1f1;
|
||||||
|
--text2: #a1a1aa;
|
||||||
|
--text3: #111111;
|
||||||
|
|
||||||
|
/* Elements */
|
||||||
|
--border: #3f3f46;
|
||||||
|
--error: #e57373;
|
||||||
|
--info: #1976d2;
|
||||||
|
--success: #81c784;
|
||||||
|
--success2: #388e3c;
|
||||||
|
|
||||||
|
/* Typo */
|
||||||
|
--typography: 'Segoe UI', 'San Francisco', 'Helvetica Neue', sans-serif;
|
||||||
|
|
||||||
|
/* Menu settings */
|
||||||
|
--menu-size: 260px;
|
||||||
|
--menu-user-avatar-bg: #555;
|
||||||
|
|
||||||
|
/* Gap */
|
||||||
|
--gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--typography);
|
||||||
|
background-color: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--rhpz-orange);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--rhpz-orange-hover);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
list-style-type: square;
|
||||||
|
}
|
||||||
|
|
||||||
|
[x-cloak] {display: none !important;}
|
||||||
|
|
||||||
|
|
||||||
|
/* BUTTONS */
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
background-color: var(--bg3);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
transition: all 0.1s;
|
||||||
|
}
|
||||||
|
.btn:hover {
|
||||||
|
background-color: var(--bg4);
|
||||||
|
border-color: var(--menu-user-avatar-bg);
|
||||||
|
}
|
||||||
|
.btn.primary {
|
||||||
|
background-color: var(--rhpz-orange);
|
||||||
|
color: var(--text3);
|
||||||
|
border-color: var(--rhpz-orange);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.btn.primary:hover {
|
||||||
|
background-color: var(--rhpz-orange-hover);
|
||||||
|
border-color: var(--rhpz-orange-hover);
|
||||||
|
}
|
||||||
|
.btn.danger {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--error);
|
||||||
|
border-color: var(--error);
|
||||||
|
}
|
||||||
|
.btn.danger:hover {
|
||||||
|
background-color: rgba(229, 115, 115, 0.1);
|
||||||
|
}
|
||||||
|
.btn.success {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--success);
|
||||||
|
border-color: var(--success);
|
||||||
|
}
|
||||||
|
.btn.success:hover {
|
||||||
|
background-color: rgba(129, 199, 132, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BLOCK */
|
||||||
|
|
||||||
|
.block {
|
||||||
|
background-color: var(--bg2);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.block.featured {
|
||||||
|
border-left: 3px solid var(--rhpz-orange);
|
||||||
|
}
|
||||||
|
.block-header {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: var(--text);
|
||||||
|
margin-bottom: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
.block-success {
|
||||||
|
background-color: var(--success);
|
||||||
|
border: 1px solid var(--success);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.block-error {
|
||||||
|
background-color: var(--error);
|
||||||
|
border: 1px solid var(--error);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PAGE */
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
font-weight: 300;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: var(--text);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TEXTS */
|
||||||
|
|
||||||
|
.whisper {
|
||||||
|
color: var(--text2);
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-title {
|
||||||
|
color: var(--text);
|
||||||
|
margin: 30px 0 15px 0;
|
||||||
|
border-left: 3px solid var(--rhpz-orange);
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote {
|
||||||
|
background-color: var(--bg);
|
||||||
|
border-left: 4px solid #1976d2;
|
||||||
|
padding: 15px;
|
||||||
|
margin-top: 30px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.btn {
|
||||||
|
padding: 7px 12px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-header {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-title {
|
||||||
|
margin: 20px 0 12px 0;
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote {
|
||||||
|
padding: 12px;
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whisper {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.btn {
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
gap: 4px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.primary, .btn.danger, .btn.success {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-header {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
padding: 2px 6px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topbar-badge {
|
||||||
|
min-width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
padding: 0 3px;
|
||||||
|
font-size: 0.6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app" style="display:flex;flex-direction: column;align-items: center;justify-content: center;">
|
||||||
|
<main id="main-wrapper">
|
||||||
|
<main id="content">
|
||||||
|
<div class="block">
|
||||||
|
<div class="page-title">Maintenance is in progress.</div>
|
||||||
|
<p>Please wait until maintenance is complete. Follow the Discord server or the forum if this is unscheduled maintenance.</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href="https://community.romhackplaza.org/" class="btn primary">Go to the forum</a>
|
||||||
|
<a href="https://discord.gg/5CKzeWmZZU" class="btn">Go to Discord</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -84,6 +84,6 @@
|
|||||||
@endforelse
|
@endforelse
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ $items->links() }}
|
{{ $items->links('modcp.pagination') }}
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
{{ $entries->links() }}
|
{{ $entries->links('modcp.pagination') }}
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -47,6 +47,6 @@
|
|||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
{{ $entries->links() }}
|
{{ $entries->links( 'modcp.pagination' ) }}
|
||||||
@endif
|
@endif
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -98,6 +98,6 @@
|
|||||||
@endforelse
|
@endforelse
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ $items->links() }}
|
{{ $items->links('modcp.pagination') }}
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<span class="modcp-stat-label">In queue</span>
|
<span class="modcp-stat-label">In queue</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
@can('moderate','\App\Models\Entry')
|
||||||
<a href="{{ route('modcp.locked') }}" class="modcp-stat-card">
|
<a href="{{ route('modcp.locked') }}" class="modcp-stat-card">
|
||||||
<div class="modcp-stat-icon"><i data-lucide="lock" size="22"></i></div>
|
<div class="modcp-stat-icon"><i data-lucide="lock" size="22"></i></div>
|
||||||
<div class="modcp-stat-info">
|
<div class="modcp-stat-info">
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
<span class="modcp-stat-label">Locked</span>
|
<span class="modcp-stat-label">Locked</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
@endcan
|
||||||
@can('is-admin')
|
@can('is-admin')
|
||||||
<a href="{{ route('modcp.draft') }}" class="modcp-stat-card">
|
<a href="{{ route('modcp.draft') }}" class="modcp-stat-card">
|
||||||
<div class="modcp-stat-icon"><i data-lucide="scissors" size="22"></i></div>
|
<div class="modcp-stat-icon"><i data-lucide="scissors" size="22"></i></div>
|
||||||
@@ -54,6 +56,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if($recentDeleted->isNotEmpty())
|
@if($recentDeleted->isNotEmpty())
|
||||||
|
@can('is-admin')
|
||||||
<div class="modcp-section-title" style="margin-top: 25px;">Recently deleted</div>
|
<div class="modcp-section-title" style="margin-top: 25px;">Recently deleted</div>
|
||||||
<div class="modcp-list">
|
<div class="modcp-list">
|
||||||
@foreach($recentDeleted as $entry)
|
@foreach($recentDeleted as $entry)
|
||||||
@@ -85,6 +88,7 @@
|
|||||||
<a href="{{ route('modcp.deleted') }}" class="modcp-list-see-all">
|
<a href="{{ route('modcp.deleted') }}" class="modcp-list-see-all">
|
||||||
See all deleted entries →
|
See all deleted entries →
|
||||||
</a>
|
</a>
|
||||||
|
@endcan
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
|||||||
34
resources/views/modcp/pagination.blade.php
Normal file
34
resources/views/modcp/pagination.blade.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php /** @var \Illuminate\Pagination\LengthAwarePaginator $paginator */ ?>
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
<div class="database-pagination">
|
||||||
|
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<button class="btn" disabled>«</button>
|
||||||
|
@else
|
||||||
|
<a class="btn" href="{{ $paginator->previousPageUrl() }}">«</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Pages --}}
|
||||||
|
@foreach ($elements as $element)
|
||||||
|
@if (is_string($element))
|
||||||
|
<button class="btn" disabled>{{ $element }}</button>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if (is_array($element))
|
||||||
|
@foreach ($element as $page => $url)
|
||||||
|
<a
|
||||||
|
class="btn {{ $page == $paginator->currentPage() ? 'active' : '' }}"
|
||||||
|
href="{{ $url }}"
|
||||||
|
>{{ $page }}</a>
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<a class="btn" href="{{ $paginator->nextPageUrl() }}">»</a>
|
||||||
|
@else
|
||||||
|
<button class="btn" disabled>»</button>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
@@ -71,6 +71,6 @@
|
|||||||
@endforelse
|
@endforelse
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ $items->links() }}
|
{{ $items->links('modcp.pagination') }}
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -154,6 +154,7 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
@if( $news->entry()->exists() || $news->relevant_link || $news->youtube_link )
|
||||||
<aside class="news-sidebar">
|
<aside class="news-sidebar">
|
||||||
|
|
||||||
@if($news->entry()->exists())
|
@if($news->entry()->exists())
|
||||||
@@ -219,6 +220,7 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</aside>
|
</aside>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
@include('entries.comments')
|
@include('entries.comments')
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
@if ($paginator->hasPages())
|
@if ($paginator->hasPages())
|
||||||
<div class="database-pagination">
|
<div class="database-pagination">
|
||||||
|
|
||||||
{{-- Précédent --}}
|
|
||||||
@if ($paginator->onFirstPage())
|
@if ($paginator->onFirstPage())
|
||||||
<button class="btn" disabled>«</button>
|
<button class="btn" disabled>«</button>
|
||||||
@else
|
@else
|
||||||
@@ -24,7 +23,6 @@
|
|||||||
@endif
|
@endif
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
||||||
{{-- Suivant --}}
|
|
||||||
@if ($paginator->hasMorePages())
|
@if ($paginator->hasMorePages())
|
||||||
<button class="btn" wire:click="nextPage">»</button>
|
<button class="btn" wire:click="nextPage">»</button>
|
||||||
@else
|
@else
|
||||||
|
|||||||
@@ -33,14 +33,14 @@ Route::name('reviews.')->controller(ReviewController::class)->group(function ()
|
|||||||
});
|
});
|
||||||
|
|
||||||
// SubmissionController.
|
// SubmissionController.
|
||||||
Route::name('submit.')->prefix('/submit')->controller(\App\Http\Controllers\SubmissionController::class)->middleware(['xf.auth', 'can:create,\App\Models\Entry'])->group(function () {
|
Route::name('submit.')->prefix('/submit')->controller(\App\Http\Controllers\SubmissionController::class)->middleware(['submissions.maintenance','xf.auth', 'can:create,\App\Models\Entry'])->group(function () {
|
||||||
Route::get('/', 'index')->name('index');
|
Route::get('/', 'index')->name('index');
|
||||||
Route::get('/{section}', 'create' )->name('create')
|
Route::get('/{section}', 'create' )->name('create')
|
||||||
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts' ]);
|
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts' ]);
|
||||||
Route::post('/{section}', 'store' )->name('store')
|
Route::post('/{section}', 'store' )->name('store')
|
||||||
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts' ]);
|
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts' ]);
|
||||||
});
|
});
|
||||||
Route::name('submit.')->prefix('/edit')->controller(\App\Http\Controllers\SubmissionController::class)->middleware(['xf.auth', 'can:update,entry'])->group(function () {
|
Route::name('submit.')->prefix('/edit')->controller(\App\Http\Controllers\SubmissionController::class)->middleware(['submissions.maintenance','xf.auth', 'can:update,entry'])->group(function () {
|
||||||
Route::get('/{section}/{entry:id}', 'edit' )->name('edit')
|
Route::get('/{section}/{entry:id}', 'edit' )->name('edit')
|
||||||
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts', 'entry' => '[0-9\-]+' ]);
|
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts', 'entry' => '[0-9\-]+' ]);
|
||||||
Route::post('/{section}/{entry:id}', 'update' )->name('update')
|
Route::post('/{section}/{entry:id}', 'update' )->name('update')
|
||||||
@@ -54,12 +54,12 @@ Route::name('news.')->controller(\App\Http\Controllers\NewsController::class)->g
|
|||||||
Route::get('/news/', 'index' )->name('index');
|
Route::get('/news/', 'index' )->name('index');
|
||||||
Route::get('/news/{news:slug}', 'show' )->name('show')->where([ 'news' => '[a-zA-Z0-9\-_]+']);
|
Route::get('/news/{news:slug}', 'show' )->name('show')->where([ 'news' => '[a-zA-Z0-9\-_]+']);
|
||||||
|
|
||||||
Route::get('/submit/news', 'create' )->name('create')->middleware(['xf.auth','can:create,\App\Models\News']);
|
Route::get('/submit/news', 'create' )->name('create')->middleware(['submissions.maintenance','xf.auth','can:create,\App\Models\News']);
|
||||||
Route::post('/submit/news', 'store' )->name('store')->middleware(['xf.auth','can:create,\App\Models\News']);
|
Route::post('/submit/news', 'store' )->name('store')->middleware(['submissions.maintenance','xf.auth','can:create,\App\Models\News']);
|
||||||
|
|
||||||
Route::get('/edit/news/{news:id}', 'edit')->name('edit')->where(['news' => '[0-9\-]+'])->middleware(['xf.auth','can:update,news']);
|
Route::get('/edit/news/{news:id}', 'edit')->name('edit')->where(['news' => '[0-9\-]+'])->middleware(['submissions.maintenance','xf.auth','can:update,news']);
|
||||||
Route::post('/edit/news/{news:id}', 'update')->name('update')->where(['news' => '[0-9\-]+'])->middleware(['xf.auth','can:update,news']);
|
Route::post('/edit/news/{news:id}', 'update')->name('update')->where(['news' => '[0-9\-]+'])->middleware(['submissions.maintenance','xf.auth','can:update,news']);
|
||||||
Route::delete('/edit/news/{news:id}', 'destroy')->name('destroy')->where(['news' => '[0-9\-]+'])->middleware(['xf.auth','can:update,news']);
|
Route::delete('/edit/news/{news:id}', 'destroy')->name('destroy')->where(['news' => '[0-9\-]+'])->middleware(['submissions.maintenance','xf.auth','can:update,news']);
|
||||||
});
|
});
|
||||||
|
|
||||||
// QueueController
|
// QueueController
|
||||||
@@ -67,32 +67,32 @@ Route::name('queue.')->prefix('/queue')->controller(\App\Http\Controllers\QueueC
|
|||||||
Route::get('/', 'index' )->name('index');
|
Route::get('/', 'index' )->name('index');
|
||||||
|
|
||||||
Route::patch('/{entry:id}/comment', 'updateComment' )
|
Route::patch('/{entry:id}/comment', 'updateComment' )
|
||||||
->middleware(['xf.auth', 'can:updateComment,entry' ] )
|
->middleware(['submissions.maintenance','xf.auth', 'can:updateComment,entry' ] )
|
||||||
->where([ 'entry' => '[0-9\-]+' ])
|
->where([ 'entry' => '[0-9\-]+' ])
|
||||||
->name('comment');
|
->name('comment');
|
||||||
|
|
||||||
Route::patch('/{entry:id}/approve', 'approve' )
|
Route::patch('/{entry:id}/approve', 'approve' )
|
||||||
->middleware(['xf.auth', 'can:approve,entry' ] )
|
->middleware(['submissions.maintenance','xf.auth', 'can:approve,entry' ] )
|
||||||
->where([ 'entry' => '[0-9\-]+' ])
|
->where([ 'entry' => '[0-9\-]+' ])
|
||||||
->name('approve');
|
->name('approve');
|
||||||
|
|
||||||
Route::patch('/{entry:id}/reject', 'reject' )
|
Route::patch('/{entry:id}/reject', 'reject' )
|
||||||
->middleware(['xf.auth', 'can:reject,entry' ] )
|
->middleware(['submissions.maintenance','xf.auth', 'can:reject,entry' ] )
|
||||||
->where([ 'entry' => '[0-9\-]+' ])
|
->where([ 'entry' => '[0-9\-]+' ])
|
||||||
->name('reject');
|
->name('reject');
|
||||||
|
|
||||||
Route::patch('/news/{news:id}/comment', 'updateComment_news' )
|
Route::patch('/news/{news:id}/comment', 'updateComment_news' )
|
||||||
->middleware(['xf.auth', 'can:updateComment,news' ] )
|
->middleware(['submissions.maintenance','xf.auth', 'can:updateComment,news' ] )
|
||||||
->where([ 'news' => '[0-9\-]+' ])
|
->where([ 'news' => '[0-9\-]+' ])
|
||||||
->name('news.comment');
|
->name('news.comment');
|
||||||
|
|
||||||
Route::patch('/news/{news:id}/approve', 'approve_news' )
|
Route::patch('/news/{news:id}/approve', 'approve_news' )
|
||||||
->middleware(['xf.auth', 'can:approve,news' ] )
|
->middleware(['submissions.maintenance','xf.auth', 'can:approve,news' ] )
|
||||||
->where([ 'news' => '[0-9\-]+' ])
|
->where([ 'news' => '[0-9\-]+' ])
|
||||||
->name('news.approve');
|
->name('news.approve');
|
||||||
|
|
||||||
Route::patch('/news/{news:id}/reject', 'reject_news' )
|
Route::patch('/news/{news:id}/reject', 'reject_news' )
|
||||||
->middleware(['xf.auth', 'can:reject,news' ] )
|
->middleware(['submissions.maintenance','xf.auth', 'can:reject,news' ] )
|
||||||
->where([ 'news' => '[0-9\-]+' ])
|
->where([ 'news' => '[0-9\-]+' ])
|
||||||
->name('news.reject');
|
->name('news.reject');
|
||||||
});
|
});
|
||||||
@@ -111,7 +111,7 @@ Route::name('tools.')->controller(\App\Http\Controllers\ToolsController::class)-
|
|||||||
Route::name('modcp.')->prefix('/modcp')->controller(\App\Http\Controllers\ModCPController::class)->middleware(['xf.auth','can:is-mod'])->group(function () {
|
Route::name('modcp.')->prefix('/modcp')->controller(\App\Http\Controllers\ModCPController::class)->middleware(['xf.auth','can:is-mod'])->group(function () {
|
||||||
|
|
||||||
Route::get('/', 'index' )->name('index');
|
Route::get('/', 'index' )->name('index');
|
||||||
Route::get('/locked-entries', 'locked' )->name('locked');
|
Route::get('/locked-entries', 'locked' )->name('locked')->middleware('can:moderate,\App\Models\Entry');
|
||||||
Route::get('/draft-entries', 'draft' )->name('draft')->middleware('can:is-admin');
|
Route::get('/draft-entries', 'draft' )->name('draft')->middleware('can:is-admin');
|
||||||
Route::get('/hidden-entries', 'hidden' )->name('hidden')->middleware('can:is-admin');
|
Route::get('/hidden-entries', 'hidden' )->name('hidden')->middleware('can:is-admin');
|
||||||
Route::get('/deleted-entries', 'deleted' )->name('deleted')->middleware('can:is-admin');
|
Route::get('/deleted-entries', 'deleted' )->name('deleted')->middleware('can:is-admin');
|
||||||
@@ -125,6 +125,8 @@ Route::name('modcp.')->prefix('/modcp')->controller(\App\Http\Controllers\ModCPC
|
|||||||
Route::resource('authors', \App\Http\Controllers\ModCP\AuthorController::class)->only(['index', 'store','update','destroy']);
|
Route::resource('authors', \App\Http\Controllers\ModCP\AuthorController::class)->only(['index', 'store','update','destroy']);
|
||||||
Route::resource('platforms', \App\Http\Controllers\ModCP\PlatformController::class )->middleware('can:is-admin')->only(['index', 'store','update','destroy']);
|
Route::resource('platforms', \App\Http\Controllers\ModCP\PlatformController::class )->middleware('can:is-admin')->only(['index', 'store','update','destroy']);
|
||||||
Route::resource('genres', \App\Http\Controllers\ModCP\GenreController::class )->middleware('can:is-admin')->only(['index', 'store','update','destroy']);
|
Route::resource('genres', \App\Http\Controllers\ModCP\GenreController::class )->middleware('can:is-admin')->only(['index', 'store','update','destroy']);
|
||||||
|
Route::resource('levels', \App\Http\Controllers\ModCP\LevelController::class )->middleware('can:is-admin')->only(['index', 'store','update','destroy']);
|
||||||
|
Route::resource('modifications', \App\Http\Controllers\ModCP\ModificationsController::class )->middleware('can:is-admin')->only(['index', 'store','update','destroy']);
|
||||||
});
|
});
|
||||||
|
|
||||||
// RedirectController
|
// RedirectController
|
||||||
|
|||||||
Reference in New Issue
Block a user