246 lines
8.5 KiB
PHP
246 lines
8.5 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers;
|
|
|
|
use App\Models\UserModel;
|
|
use App\Models\OrganizationModel;
|
|
use App\Models\OrganizationUserModel;
|
|
use App\Services\AccessService;
|
|
|
|
/**
|
|
* ProfileController - Управление профилем пользователя
|
|
*/
|
|
class Profile extends BaseController
|
|
{
|
|
protected UserModel $userModel;
|
|
protected OrganizationModel $orgModel;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->userModel = new UserModel();
|
|
$this->orgModel = new OrganizationModel();
|
|
}
|
|
|
|
/**
|
|
* Главная страница профиля
|
|
*/
|
|
public function index()
|
|
{
|
|
$userId = $this->getCurrentUserId();
|
|
$user = $this->userModel->find($userId);
|
|
|
|
return $this->renderTwig('profile/index', [
|
|
'title' => 'Профиль',
|
|
'user' => $user,
|
|
'active_tab' => 'general',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Вкладка "Организации"
|
|
*/
|
|
public function organizations()
|
|
{
|
|
$userId = $this->getCurrentUserId();
|
|
$user = $this->userModel->find($userId);
|
|
$currentOrgId = $this->session->get('active_org_id');
|
|
|
|
// Получаем все организации пользователя
|
|
$orgUserModel = $this->getOrgUserModel();
|
|
$memberships = $orgUserModel->where('user_id', $userId)->findAll();
|
|
|
|
$orgIds = array_column($memberships, 'organization_id');
|
|
$organizations = [];
|
|
|
|
if (!empty($orgIds)) {
|
|
$organizations = $this->orgModel->whereIn('id', $orgIds)->findAll();
|
|
}
|
|
|
|
// Объединяем данные
|
|
$orgList = [];
|
|
foreach ($organizations as $org) {
|
|
// Находим соответствующий membership
|
|
$membership = null;
|
|
foreach ($memberships as $m) {
|
|
if ($m['organization_id'] == $org['id']) {
|
|
$membership = $m;
|
|
break;
|
|
}
|
|
}
|
|
|
|
$orgList[] = [
|
|
'id' => $org['id'],
|
|
'name' => $org['name'],
|
|
'type' => $org['type'],
|
|
'role' => $membership['role'] ?? 'guest',
|
|
'status' => $membership['status'] ?? 'active',
|
|
'joined_at' => $membership['joined_at'] ?? null,
|
|
'is_owner' => ($membership['role'] ?? '') === 'owner',
|
|
'is_current_org' => ((int) $org['id'] === (int) $currentOrgId),
|
|
];
|
|
}
|
|
|
|
return $this->renderTwig('profile/organizations', [
|
|
'title' => 'Мои организации',
|
|
'user' => $user,
|
|
'organizations' => $orgList,
|
|
'active_tab' => 'organizations',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Вкладка "Безопасность"
|
|
*/
|
|
public function security()
|
|
{
|
|
$userId = $this->getCurrentUserId();
|
|
$user = $this->userModel->find($userId);
|
|
|
|
return $this->renderTwig('profile/security', [
|
|
'title' => 'Безопасность',
|
|
'user' => $user,
|
|
'active_tab' => 'security',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Обновление имени пользователя
|
|
*/
|
|
public function updateName()
|
|
{
|
|
$userId = $this->getCurrentUserId();
|
|
$name = trim($this->request->getPost('name'));
|
|
|
|
if (empty($name)) {
|
|
$this->session->setFlashdata('error', 'Имя обязательно для заполнения');
|
|
return redirect()->to('/profile');
|
|
}
|
|
|
|
if (strlen($name) < 3) {
|
|
$this->session->setFlashdata('error', 'Имя должно содержать минимум 3 символа');
|
|
return redirect()->to('/profile');
|
|
}
|
|
|
|
$this->userModel->update($userId, ['name' => $name]);
|
|
$this->session->set('name', $name);
|
|
$this->session->setFlashdata('success', 'Имя успешно обновлено');
|
|
|
|
return redirect()->to('/profile');
|
|
}
|
|
|
|
/**
|
|
* Загрузка аватара
|
|
*/
|
|
public function uploadAvatar()
|
|
{
|
|
$userId = $this->getCurrentUserId();
|
|
$file = $this->request->getFile('avatar');
|
|
|
|
if (!$file || !$file->isValid()) {
|
|
$this->session->setFlashdata('error', 'Ошибка загрузки файла');
|
|
return redirect()->to('/profile');
|
|
}
|
|
|
|
// Валидация
|
|
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
|
|
$maxSize = 2 * 1024 * 1024; // 2MB
|
|
|
|
if (!in_array($file->getMimeType(), $allowedTypes)) {
|
|
$this->session->setFlashdata('error', 'Разрешены только файлы JPG, PNG и GIF');
|
|
return redirect()->to('/profile');
|
|
}
|
|
|
|
if ($file->getSize() > $maxSize) {
|
|
$this->session->setFlashdata('error', 'Максимальный размер файла - 2 МБ');
|
|
return redirect()->to('/profile');
|
|
}
|
|
|
|
// Создаём директорию для аватаров если нет
|
|
$uploadPath = ROOTPATH . 'public/uploads/avatars';
|
|
if (!is_dir($uploadPath)) {
|
|
mkdir($uploadPath, 0755, true);
|
|
}
|
|
|
|
// Генерируем уникальное имя файла
|
|
$extension = $file->getClientExtension();
|
|
$newFileName = 'avatar_' . $userId . '_' . time() . '.' . $extension;
|
|
|
|
// Перемещаем файл
|
|
$file->move($uploadPath, $newFileName);
|
|
|
|
// Удаляем старый аватар если был
|
|
$user = $this->userModel->find($userId);
|
|
if (!empty($user['avatar']) && file_exists($uploadPath . '/' . $user['avatar'])) {
|
|
@unlink($uploadPath . '/' . $user['avatar']);
|
|
}
|
|
|
|
// Обновляем путь к аватару в базе
|
|
$this->userModel->update($userId, ['avatar' => $newFileName]);
|
|
|
|
$this->session->setFlashdata('success', 'Аватар успешно загружен');
|
|
return redirect()->to('/profile');
|
|
}
|
|
|
|
/**
|
|
* Смена пароля
|
|
*/
|
|
public function changePassword()
|
|
{
|
|
$userId = $this->getCurrentUserId();
|
|
$user = $this->userModel->find($userId);
|
|
|
|
$currentPassword = $this->request->getPost('current_password');
|
|
$newPassword = $this->request->getPost('new_password');
|
|
$confirmPassword = $this->request->getPost('confirm_password');
|
|
|
|
// Валидация
|
|
if (empty($currentPassword)) {
|
|
$this->session->setFlashdata('error', 'Введите текущий пароль');
|
|
return redirect()->to('/profile/security');
|
|
}
|
|
|
|
if (empty($newPassword)) {
|
|
$this->session->setFlashdata('error', 'Введите новый пароль');
|
|
return redirect()->to('/profile/security');
|
|
}
|
|
|
|
if (strlen($newPassword) < 6) {
|
|
$this->session->setFlashdata('error', 'Новый пароль должен содержать минимум 6 символов');
|
|
return redirect()->to('/profile/security');
|
|
}
|
|
|
|
if ($newPassword !== $confirmPassword) {
|
|
$this->session->setFlashdata('error', 'Пароли не совпадают');
|
|
return redirect()->to('/profile/security');
|
|
}
|
|
|
|
// Проверяем текущий пароль
|
|
if (!password_verify($currentPassword, $user['password'])) {
|
|
$this->session->setFlashdata('error', 'Неверный текущий пароль');
|
|
return redirect()->to('/profile/security');
|
|
}
|
|
|
|
// Обновляем пароль
|
|
$this->userModel->update($userId, ['password' => $newPassword]);
|
|
|
|
// Завершаем все сессии пользователя (кроме текущей)
|
|
$this->endAllUserSessions($userId);
|
|
|
|
$this->session->setFlashdata('success', 'Пароль успешно изменён. Для безопасности вы будете разлогинены на всех устройствах.');
|
|
return redirect()->to('/logout');
|
|
}
|
|
|
|
/**
|
|
* Завершение всех сессий пользователя
|
|
*/
|
|
private function endAllUserSessions(int $userId): void
|
|
{
|
|
// Удаляем все remember-токены пользователя
|
|
$db = \Config\Database::connect();
|
|
$db->table('remember_tokens')->where('user_id', $userId)->delete();
|
|
|
|
// Регенерируем ID текущей сессии
|
|
$this->session->regenerate(true);
|
|
}
|
|
}
|