invitationService = new InvitationService(); } /** * Страница принятия/отклонения приглашения */ public function accept(string $token) { $invitation = $this->invitationService->orgUserModel->findByInviteToken($token); if (!$invitation) { // Проверяем, есть ли приглашение с таким токеном (может быть истекшим) $db = \Config\Database::connect(); $expiredInvitation = $db->table('organization_users') ->where('invite_token', $token) ->get() ->getRowArray(); if ($expiredInvitation && !empty($expiredInvitation['invite_expires_at'])) { $expiredAt = strtotime($expiredInvitation['invite_expires_at']); $isExpired = $expiredAt < time(); return $this->renderTwig('organizations/invitation_expired', [ 'title' => $isExpired ? 'Приглашение истекло' : 'Приглашение недействительно', 'expired' => $isExpired, 'expired_at' => $expiredInvitation['invite_expires_at'] ?? null, ]); } return $this->renderTwig('organizations/invitation_expired', [ 'title' => 'Приглашение недействительно', ]); } // Получаем данные организации $orgModel = new OrganizationModel(); $organization = $orgModel->find($invitation['organization_id']); // Получаем данные приглашающего $invitedByUser = null; if ($invitation['invited_by']) { $userModel = new UserModel(); $invitedByUser = $userModel->find($invitation['invited_by']); } // Определяем, авторизован ли пользователь $currentUserId = session()->get('user_id'); $isLoggedIn = !empty($currentUserId); // Если пользователь авторизован - проверяем, тот ли это email $emailMatches = true; if ($isLoggedIn && $invitation['user_id']) { $currentUser = (new UserModel())->find($currentUserId); // Проверяем, тот ли пользователь (по ID или email) $emailMatches = ($currentUserId == $invitation['user_id']); } // Метка роли $roleLabels = [ 'owner' => 'Владелец', 'admin' => 'Администратор', 'manager' => 'Менеджер', 'guest' => 'Гость', ]; return $this->renderTwig('organizations/invitation_accept', [ 'title' => 'Приглашение в ' . ($organization['name'] ?? 'организацию'), 'token' => $token, 'organization' => $organization, 'role' => $invitation['role'], 'role_label' => $roleLabels[$invitation['role']] ?? $invitation['role'], 'invited_by' => $invitedByUser, 'invited_at' => $invitation['invited_at'], 'is_logged_in' => $isLoggedIn, 'email_matches' => $emailMatches, 'current_user_id'=> $currentUserId, ]); } /** * Обработка принятия приглашения */ public function processAccept() { $token = $this->request->getPost('token'); $action = $this->request->getPost('action'); if ($action === 'decline') { return $this->decline($token); } $userId = session()->get('user_id'); // Если пользователь не авторизован - редирект на страницу создания пароля if (!$userId) { return redirect()->to('/invitation/complete/' . $token); } // Принимаем приглашение $result = $this->invitationService->acceptInvitation($token, $userId); if (!$result['success']) { session()->setFlashdata('error', $result['message']); return redirect()->to('/invitation/accept/' . $token); } session()->setFlashdata('success', 'Вы приняли приглашение в организацию "' . $result['organization_name'] . '"'); // Переключаем организацию и редиректим на главную session()->set('active_org_id', $result['organization_id']); (new AccessService())->resetCache(); return redirect()->to('/'); } /** * Отклонение приглашения */ public function decline(string $token) { $result = $this->invitationService->declineInvitation($token); if (!$result['success']) { session()->setFlashdata('error', $result['message']); } else { session()->setFlashdata('info', 'Приглашение отклонено'); } return redirect()->to('/'); } /** * Страница завершения регистрации (для новых пользователей) */ public function complete(string $token) { $invitation = $this->invitationService->orgUserModel->findByInviteToken($token); if (!$invitation) { // Проверяем, есть ли приглашение с таким токеном (может быть истекшим) $db = \Config\Database::connect(); $expiredInvitation = $db->table('organization_users') ->where('invite_token', $token) ->get() ->getRowArray(); if ($expiredInvitation && !empty($expiredInvitation['invite_expires_at'])) { $expiredAt = strtotime($expiredInvitation['invite_expires_at']); $isExpired = $expiredAt < time(); return $this->renderTwig('organizations/invitation_expired', [ 'title' => $isExpired ? 'Приглашение истекло' : 'Приглашение недействительно', 'expired' => $isExpired, 'expired_at' => $expiredInvitation['invite_expires_at'] ?? null, ]); } return $this->renderTwig('organizations/invitation_expired', [ 'title' => 'Приглашение недействительно', ]); } // Если пользователь уже авторизован и это его приглашение $userId = session()->get('user_id'); if ($userId && $invitation['user_id'] == $userId) { return redirect()->to('/invitation/accept/' . $token); } // Получаем данные организации $orgModel = new OrganizationModel(); $organization = $orgModel->find($invitation['organization_id']); // Метка роли $roleLabels = [ 'owner' => 'Владелец', 'admin' => 'Администратор', 'manager' => 'Менеджер', 'guest' => 'Гость', ]; return $this->renderTwig('organizations/invitation_complete', [ 'title' => 'Завершение регистрации', 'token' => $token, 'email' => $invitation['user_id'] ? '' : '', // Email возьмем из теневой записи 'organization' => $organization, 'role' => $invitation['role'], 'role_label' => $roleLabels[$invitation['role']] ?? $invitation['role'], ]); } /** * Обработка завершения регистрации */ public function processComplete() { $token = $this->request->getPost('token'); $name = $this->request->getPost('name'); $password = $this->request->getPost('password'); $passwordConfirm = $this->request->getPost('password_confirm'); // Валидация $errors = []; if (empty($name) || strlen($name) < 2) { $errors[] = 'Имя должно содержать минимум 2 символа'; } if (empty($password) || strlen($password) < 8) { $errors[] = 'Пароль должен содержать минимум 8 символов'; } if ($password !== $passwordConfirm) { $errors[] = 'Пароли не совпадают'; } if (!empty($errors)) { return redirect()->back()->withInput()->with('errors', $errors); } $invitation = $this->invitationService->orgUserModel->findByInviteToken($token); if (!$invitation) { return redirect()->to('/'); } // Находим теневую запись пользователя $userModel = new UserModel(); // Если user_id указан - обновляем существующего пользователя if ($invitation['user_id']) { $user = $userModel->find($invitation['user_id']); if (!$user) { session()->setFlashdata('error', 'Пользователь не найден'); return redirect()->to('/invitation/complete/' . $token); } // Обновляем профиль $userModel->update($user['id'], [ 'name' => $name, 'password' => $password, ]); $userId = $user['id']; } else { // Создаем нового пользователя (теневой + реальный) // На самом деле у нас уже есть запись в users - нужно её "активировать" $shadowUsers = $userModel->where('email', $userModel->getFindByEmail($invitation['organization_id']))->findAll(); // Логика для теневых пользователей требует доработки session()->setFlashdata('error', 'Ошибка регистрации'); return redirect()->to('/invitation/complete/' . $token); } // Авторизуем пользователя $user = $userModel->find($userId); $this->loginUser($user); // Принимаем приглашение $result = $this->invitationService->acceptInvitation($token, $userId); if (!$result['success']) { session()->setFlashdata('error', $result['message']); return redirect()->to('/'); } session()->setFlashdata('success', 'Добро пожаловать! Вы успешно зарегистрировались и приняли приглашение.'); // Переключаем организацию session()->set('active_org_id', $result['organization_id']); (new AccessService())->resetCache(); return redirect()->to('/'); } /** * Авторизация пользователя после регистрации */ protected function loginUser(array $user): void { session()->set([ 'user_id' => $user['id'], 'email' => $user['email'], 'name' => $user['name'], 'logged_in' => true, ]); } }