urls->docsHomeUrl(), PHP_URL_HOST); $targetHost = (string) parse_url($redirectTo, PHP_URL_HOST); $targetPath = (string) parse_url($redirectTo, PHP_URL_PATH); $targetQuery = (string) parse_url($redirectTo, PHP_URL_QUERY); if ($appHost === '' || $targetHost === '' || strcasecmp($appHost, $targetHost) !== 0) { return null; } $redirectTo = $targetPath !== '' ? $targetPath : '/'; if ($targetQuery !== '') { $redirectTo .= '?'.$targetQuery; } } if (! str_starts_with($redirectTo, '/')) { $redirectTo = '/'.ltrim($redirectTo, '/'); } if (str_starts_with($redirectTo, '//')) { return null; } return $redirectTo; } /** * Highest-priority dashboard route matching any of the role names/slugs. * * @param list $roleNames */ public function dashboardRouteForRoles(array $roleNames): string { if ($roleNames === []) { return self::FALLBACK_DASHBOARD; } $rows = Role::findByNamesOrSlugs($roleNames); if ($rows !== []) { $route = $rows[0]->dashboard_route ?? null; if (is_string($route) && $route !== '') { return $route; } } return self::FALLBACK_DASHBOARD; } /** * Apply CI-style preference keys into session (style/menu colors). */ public function applyStylePreferencesToSession(int $userId): void { if ($userId <= 0) { return; } $prefs = Preferences::query()->where('user_id', $userId)->first(); if (! $prefs) { return; } $styleColor = (string) ($prefs->style_color ?? ''); if ($styleColor !== '') { session()->put('style_color', $styleColor); } $menuColor = (string) ($prefs->menu_color ?? ''); if ($menuColor === 'custom') { session()->put('menu_color', 'custom'); session()->put('menu_custom_bg', (string) ($prefs->menu_custom_bg ?? '#0f172a')); session()->put('menu_custom_text', (string) ($prefs->menu_custom_text ?? '#ffffff')); session()->put('menu_custom_mode', (string) ($prefs->menu_custom_mode ?? 'dark')); } elseif ($menuColor !== '') { session()->put('menu_color', $menuColor); } } /** * Populate Laravel web auth + CI-compatible session keys. * * @param list $roleNames */ public function writeCiAuthenticatedSession(User $user, array $roleNames): void { Auth::guard('web')->login($user); $schoolYear = Configuration::getConfig('school_year'); $semester = Configuration::getConfig('semester'); $teacherContext = $user->teacherSessionContext(); session([ 'user_id' => $user->id, 'user_email' => $user->email, 'user_name' => trim(($user->firstname ?? '').' '.($user->lastname ?? '')), 'user_type' => $user->user_type ?? null, 'is_logged_in' => true, 'login_time' => time(), 'last_activity' => time(), 'roles' => $roleNames, 'semester' => $semester, 'school_year' => $schoolYear, 'class_section_id' => $teacherContext['class_section_id'], 'class_section_name' => $teacherContext['class_section_name'], ]); $this->applyStylePreferencesToSession((int) $user->id); } /** * Close open login_activity row(s) without logout_time for this user (CI parity). */ public function logLogout(?int $userId, ?string $email): void { if (! $userId || ! $email) { return; } LoginActivity::query() ->where('user_id', $userId) ->whereNull('logout_time') ->update([ 'logout_time' => utc_now(), 'email' => $email, ]); } /** * Determine next navigation after credentials validated (single vs multi-role). * * @return array{kind: string, redirect_url?: string|null, roles?: array, reason?: string} */ public function resolvePostLoginDestination(User $user, ?string $sanitizedRedirect): array { $roleNames = $user->roleNamesLikeCodeIgniter(); if ($roleNames === []) { Log::notice('session_login: user has no roles', ['user_id' => $user->id]); return [ 'kind' => 'no_roles', 'reason' => 'Role not assigned. Please contact support.', ]; } $this->writeCiAuthenticatedSession($user, $roleNames); if (count($roleNames) === 1) { session()->put('role', $roleNames[0]); if ($sanitizedRedirect !== null) { return ['kind' => 'redirect', 'redirect_url' => $sanitizedRedirect]; } return [ 'kind' => 'redirect', 'redirect_url' => $this->dashboardRouteForRoles([$roleNames[0]]), ]; } if ($sanitizedRedirect !== null) { session()->put('post_login_redirect', $sanitizedRedirect); } else { session()->forget('post_login_redirect'); } return [ 'kind' => 'select_role', 'roles' => array_values($roleNames), 'redirect_url' => $this->urls->webSelectRoleUrl(false), ]; } }