*/ public Collection $settings; public string $title = ''; public string $content = ''; public string $meta_keywords = ''; public string $meta_title = ''; public string $meta_description = ''; /** * Current page the user is browsing. ie browse. */ public string $page = ''; public User $userdata; /** * User's theme. */ protected string $theme = 'Gentele'; /** * View data array for Blade templates * * @var array */ protected array $viewData = []; /** * BasePageController constructor. * * @throws \Exception */ public function __construct() { $this->middleware(['auth', 'web', '2fa'])->except('api', 'contact', 'showContactForm', 'callback', 'btcPayCallback', 'getNzb', 'terms', 'privacyPolicy', 'capabilities', 'movie', 'apiSearch', 'tv', 'details', 'failed', 'showRssDesc', 'fullFeedRss', 'categoryFeedRss', 'cartRss', 'myMoviesRss', 'myShowsRss', 'trendingMoviesRss', 'trendingShowsRss', 'release', 'reset', 'showLinkRequestForm', 'showStatusPage'); // Load settings as collection with caching (5 minutes) $this->settings = $this->rememberWithCacheFallback('site_settings', 300, function () { return Settings::query()->pluck('value', 'name'); }); // Initialize view data FIRST with serverroot $this->viewData = [ 'serverroot' => url('/'), ]; // Then add the converted settings array as 'site' with caching $this->viewData['site'] = $this->rememberWithCacheFallback('site_settings_converted', 300, function () { return $this->settings->map(function ($value) { return Settings::convertValue($value); })->all(); }); // Initialize userdata property for controllers that need it $this->middleware(function ($request, $next) { if (Auth::check()) { $userId = Auth::id(); $this->userdata = User::find($userId); if (! $this->userdata?->hasVerifiedEmail()) { return $next($request); } // Cache category exclusions per user (5 minutes) $this->userdata->categoryexclusions = $this->rememberWithCacheFallback( User::categoryExclusionCacheKey($userId), 300, fn () => User::getCategoryExclusionById($userId) ); } return $next($request); }); } /** * Use the cache when available; if the store is unreachable (e.g. Redis down), run the callback directly. * * @param callable(): mixed $callback */ private function rememberWithCacheFallback(string $key, int|\DateInterval $ttl, callable $callback): mixed { try { return Cache::remember($key, $ttl, $callback); } catch (\Throwable $e) { if (config('app.debug')) { Log::debug('BasePageController cache bypassed: '.$e->getMessage()); } return $callback(); } } /** * @return LengthAwarePaginator */ public function paginate(mixed $query, mixed $totalCount, mixed $items, mixed $page, mixed $path, mixed $reqQuery): LengthAwarePaginator { return new LengthAwarePaginator($query, $totalCount, $items, $page, ['path' => $path, 'query' => $reqQuery]); } protected function resolvePage(Request $request, string $key = 'page'): int { $page = $request->input($key, 1); if (! is_scalar($page)) { return 1; } $page = (string) $page; if ($page === '' || preg_match('/^\d+$/', $page) !== 1) { return 1; } return max(1, (int) $page); } /** * @param array $ordering */ protected function resolveOrderBy(Request $request, array $ordering, string $key = 'ob'): string { $orderByInput = $request->input($key, ''); $orderBy = is_scalar($orderByInput) ? (string) $orderByInput : ''; return \in_array($orderBy, $ordering, true) ? $orderBy : ''; } protected function paginationOffset(int $page, int $perPage): int { return ($page - 1) * $perPage; } /** * @param array $ordering * @return array */ protected function buildOrderByUrls(array $ordering, string $basePath, string $prefix = 'orderby'): array { $orderByUrls = []; foreach ($ordering as $orderType) { $orderByUrls[$prefix.$orderType] = url($basePath.'?ob='.$orderType); } return $orderByUrls; } protected function scalarInput(Request $request, string $key, string $default = ''): string { $value = $request->input($key, $default); return is_scalar($value) ? (string) $value : $default; } protected function integerInput(Request $request, string $key, int $default = 0): int { $value = $this->scalarInput($request, $key, (string) $default); return preg_match('/^-?\d+$/', $value) === 1 ? (int) $value : $default; } /** * @return array */ protected function arrayInput(Request $request, string $key): array { $value = $request->input($key, []); return \is_array($value) ? $value : []; } protected function localReturnUrl(Request $request, string $fallback, string $key = 'from'): string { $from = $this->scalarInput($request, $key); if ($from === '') { return url($fallback); } if (filter_var($from, FILTER_VALIDATE_URL) !== false) { return parse_url($from, PHP_URL_HOST) === $request->getHost() ? $from : url($fallback); } return url($from); } /** * Check if request is POST. */ public function isPostBack(Request $request): bool { return $request->isMethod('POST'); } /** * Show 404 page. * * @param string|null $message */ public function show404($message = null): View { if ($message !== null) { return view('errors.404')->with('Message', $message); } return view('errors.404'); } /** * Set admin preferences. */ public function setAdminPrefs(): void { $this->viewData['catClass'] = Category::class; } /** * Output the page using Blade views. */ public function pagerender(): View { return view('layouts.main', $this->viewData); } /** * Output a page using the admin template. * * @throws \Exception */ public function adminrender(): View { return view('layouts.admin', $this->viewData); } /** * @throws \Exception */ public function adminBasePage(): View { $this->setAdminPrefs(); $this->viewData = array_merge($this->viewData, [ 'meta_title' => 'Admin Home', 'meta_description' => 'Admin home page', ]); return $this->adminrender(); } }