getCurrentUser(); if ($currentUser->isSuperAdmin()) { $users = User::all(); } else { // Admins only see users from their company $users = User::where('users_bus_id', $currentUser->users_bus_id)->get(); } return $this->renderAdminView('users/views/admin/index.twig', [ 'users' => $users, 'title' => __('users.titles.management') ]); } public function create(Request $request): Response { $error = $request->query->get('error'); $errorMessage = $error ? $this->getErrorMessage($error) : null; return $this->renderAdminView('users/views/admin/form.twig', [ 'title' => __('users.titles.create'), 'error' => $errorMessage, 'target_user' => null ]); } public function store(Request $request): Response { $currentUserId = $_SESSION['user_id'] ?? null; $currentUserRole = $_SESSION['user_role'] ?? null; $currentUserBus = $_SESSION['user_idbus'] ?? null; $data = $request->request->all(); // Basic validation if (empty($data['name']) || empty($data['email']) || empty($data['password'])) { return new RedirectResponse(url('admin/users/create?error=missing_fields')); } // Email format validation if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) { return new RedirectResponse(url('admin/users/create?error=invalid_email')); } // Check if email exists if (User::where('users_email', $data['email'])->exists()) { return new RedirectResponse(url('admin/users/create?error=email_exists')); } // Password strength validation if (strlen($data['password']) < 8 || !preg_match('/[A-Za-z]/', $data['password']) || !preg_match('/[0-9]/', $data['password'])) { return new RedirectResponse(url('admin/users/create?error=weak_password')); } $requestedRole = $data['role'] ?? 'user'; // Prevent non-super_admin from creating super_admin if ($currentUserRole !== 'super_admin' && $requestedRole === 'super_admin') { $requestedRole = 'user'; } $user = new User(); $user->users_name = $data['name']; $user->users_email = $data['email']; $user->users_password = $data['password']; $user->users_role = $requestedRole; $user->users_status = $data['status'] ?? 'active'; $user->users_bus_id = $currentUserRole === 'super_admin' ? ($data['bus_id'] ?? $currentUserBus) : $currentUserBus; $user->save(); if ($request->files->has('avatar')) { $file = $request->files->get('avatar'); if ($file && $file->isValid()) { $allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; if (in_array($file->getMimeType(), $allowedMimes)) { $uploadDir = dirname(__DIR__, 4) . '/storage/uploads/avatars'; if (!file_exists($uploadDir)) { mkdir($uploadDir, 0755, true); } $formatSetting = strtolower((string) get_env('IMG_FORMAT', 'webp')); $format = $formatSetting === 'auto' ? 'webp' : $formatSetting; $quality = env_int('IMG_QUALITY', 80); $resolutionPercent = env_int('IMG_RESOLUTION', 100); $image = Image::make($file->getPathname()); $width = $image->width(); $height = $image->height(); if ($resolutionPercent > 0 && $resolutionPercent < 100) { $newW = (int) round($width * $resolutionPercent / 100); $newH = (int) round($height * $resolutionPercent / 100); $image->resize($newW, $newH, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }); } $ext = $format === 'jpeg' || $format === 'jpg' ? 'jpg' : ($format === 'png' ? 'png' : ($format === 'gif' ? 'gif' : 'webp')); $filename = 'avatar_' . $user->users_id . '_' . time() . '.' . $ext; if ($format === 'jpeg' || $format === 'jpg') { $image->interlace(true); $image->encode('jpeg', max(1, min(100, $quality))); } elseif ($format === 'png') { $pngCompression = max(0, min(9, (int) round((100 - max(1, min(100, $quality))) / 10))); $image->encode('png', $pngCompression); } elseif ($format === 'gif') { $image->encode('gif'); } else { $image->encode('webp', max(1, min(100, $quality))); } $image->save($uploadDir . '/' . $filename); $user->users_avatar = $filename; $user->save(); } } } // Restore session to ensure super_admin remains logged $_SESSION['user_id'] = $currentUserId; $_SESSION['user_role'] = $currentUserRole; $_SESSION['user_idbus'] = $currentUserBus; return new RedirectResponse(url('admin/users')); } public function show(Request $request, string $id): Response { $currentUser = $this->getCurrentUser(); $query = User::where('users_id', $id); if (!$currentUser->isSuperAdmin()) { $query->where('users_bus_id', $currentUser->users_bus_id); } $user = $query->first(); if (!$user) { return new RedirectResponse(url('admin/users')); } return $this->renderAdminView('users/views/admin/show.twig', [ 'user' => $user, 'title' => 'User Details' ]); } public function edit(Request $request, string $id): Response { $currentUser = $this->getCurrentUser(); $query = User::where('users_id', $id); if (!$currentUser->isSuperAdmin()) { $query->where('users_bus_id', $currentUser->users_bus_id); } $targetUser = $query->first(); if (!$targetUser) { return new RedirectResponse(url('admin/users')); } $error = $request->query->get('error'); $errorMessage = $error ? $this->getErrorMessage($error) : null; return $this->renderAdminView('users/views/admin/form.twig', [ 'target_user' => $targetUser, 'title' => 'Edit User', 'error' => $errorMessage ]); } public function update(Request $request, string $id): Response { $currentUser = $this->getCurrentUser(); $currentUserId = $_SESSION['user_id'] ?? null; $currentUserRole = $_SESSION['user_role'] ?? null; $currentUserBus = $_SESSION['user_idbus'] ?? null; $query = User::where('users_id', $id); if (!$currentUser->isSuperAdmin()) { $query->where('users_bus_id', $currentUser->users_bus_id); } $user = $query->first(); if (!$user) { return new RedirectResponse(url('admin/users')); } $data = $request->request->all(); // Basic validation if (empty($data['name']) || empty($data['email'])) { return new RedirectResponse(url('admin/users/' . $id . '/edit?error=missing_fields')); } // Email format validation if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) { return new RedirectResponse(url('admin/users/' . $id . '/edit?error=invalid_email')); } // Check if email exists (exclude current user) if ($data['email'] !== $user->users_email && User::where('users_email', $data['email'])->exists()) { return new RedirectResponse(url('admin/users/' . $id . '/edit?error=email_exists')); } $requestedRole = $data['role'] ?? $user->users_role; // Prevent non-super_admin from promoting to super_admin or changing a super_admin if ($currentUserRole !== 'super_admin') { if ($requestedRole === 'super_admin' || $user->users_role === 'super_admin') { $requestedRole = $user->users_role; } } $user->users_name = $data['name']; $user->users_email = $data['email']; $user->users_role = $requestedRole; $user->users_status = $data['status'] ?? 'active'; // Only super_admin can change company of a user if ($currentUserRole === 'super_admin' && isset($data['bus_id'])) { $user->users_bus_id = $data['bus_id']; } if (!empty($data['password'])) { // Password strength validation if (strlen($data['password']) < 8 || !preg_match('/[A-Za-z]/', $data['password']) || !preg_match('/[0-9]/', $data['password'])) { return new RedirectResponse(url('admin/users/' . $id . '/edit?error=weak_password')); } $user->users_password = $data['password']; // Mutator handles hashing } if ($request->files->has('avatar')) { $file = $request->files->get('avatar'); if ($file && $file->isValid()) { $allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; if (in_array($file->getMimeType(), $allowedMimes)) { $uploadDir = dirname(__DIR__, 4) . '/storage/uploads/avatars'; if (!file_exists($uploadDir)) { mkdir($uploadDir, 0755, true); } $formatSetting = strtolower((string) get_env('IMG_FORMAT', 'webp')); $format = $formatSetting === 'auto' ? 'webp' : $formatSetting; $quality = env_int('IMG_QUALITY', 80); $resolutionPercent = env_int('IMG_RESOLUTION', 100); $image = Image::make($file->getPathname()); $width = $image->width(); $height = $image->height(); if ($resolutionPercent > 0 && $resolutionPercent < 100) { $newW = (int) round($width * $resolutionPercent / 100); $newH = (int) round($height * $resolutionPercent / 100); $image->resize($newW, $newH, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }); } $ext = $format === 'jpeg' || $format === 'jpg' ? 'jpg' : ($format === 'png' ? 'png' : ($format === 'gif' ? 'gif' : 'webp')); $filename = 'avatar_' . $user->users_id . '_' . time() . '.' . $ext; if ($format === 'jpeg' || $format === 'jpg') { $image->interlace(true); $image->encode('jpeg', max(1, min(100, $quality))); } elseif ($format === 'png') { $pngCompression = max(0, min(9, (int) round((100 - max(1, min(100, $quality))) / 10))); $image->encode('png', $pngCompression); } elseif ($format === 'gif') { $image->encode('gif'); } else { $image->encode('webp', max(1, min(100, $quality))); } $image->save($uploadDir . '/' . $filename); if ($user->users_avatar && file_exists($uploadDir . '/' . $user->users_avatar)) { unlink($uploadDir . '/' . $user->users_avatar); } $user->users_avatar = $filename; } } } $user->save(); // Restore session to ensure super_admin remains logged $_SESSION['user_id'] = $currentUserId; $_SESSION['user_role'] = $currentUserRole; $_SESSION['user_idbus'] = $currentUserBus; return new RedirectResponse(url('admin/users')); } public function editPermissions(Request $request, string $id): Response { $currentUser = $this->getCurrentUser(); if (!$currentUser || (!$currentUser->isAdmin() && !$currentUser->isSuperAdmin())) { return new RedirectResponse(url('admin/dashboard?error=unauthorized')); } $user = User::find($id); if (!$user) { return new RedirectResponse(url('admin/users')); } // Check if Admin is from the same company if (!$currentUser->isSuperAdmin() && $user->users_bus_id !== $currentUser->users_bus_id) { return new RedirectResponse(url('admin/users?error=unauthorized')); } // Get enabled modules for the company $modules = $this->getEnabledModules($user); // Get current user permissions $userPermissions = UserPermission::where('user_permissions_user_id', $user->users_id) ->where('user_permissions_bus_id', $user->users_bus_id) ->pluck('user_permissions_permission') ->toArray(); return $this->renderAdminView('users/views/admin/permissions.twig', [ 'target_user' => $user, 'modules' => $modules, 'user_permissions' => $userPermissions, 'title' => __('users.headers.user_permissions') ]); } public function updatePermissions(Request $request, string $id): Response { $currentUser = $this->getCurrentUser(); if (!$currentUser || (!$currentUser->isAdmin() && !$currentUser->isSuperAdmin())) { return new RedirectResponse(url('admin/dashboard?error=unauthorized')); } $user = User::find($id); if (!$user) { return new RedirectResponse(url('admin/users')); } // Check if Admin is from the same company if (!$currentUser->isSuperAdmin() && $user->users_bus_id !== $currentUser->users_bus_id) { return new RedirectResponse(url('admin/users?error=unauthorized')); } $permissions = $request->request->all()['permissions'] ?? []; // Remove existing permissions for this user in this company UserPermission::where('user_permissions_user_id', $user->users_id) ->where('user_permissions_bus_id', $user->users_bus_id) ->delete(); // Add new permissions foreach ($permissions as $permission) { $userPermission = new UserPermission(); $userPermission->user_permissions_user_id = $user->users_id; $userPermission->user_permissions_bus_id = $user->users_bus_id; $userPermission->user_permissions_permission = $permission; $userPermission->user_permissions_granted_by = $currentUser->users_id; $userPermission->save(); } return new RedirectResponse(url('admin/users/' . $id . '?success=permissions_updated')); } private function getErrorMessage(string $error): string { $messages = [ 'missing_fields' => 'Please fill in all required fields.', 'email_exists' => 'This email address is already registered.', 'invalid_email' => 'Please enter a valid email address.', 'weak_password' => 'Password must be at least 8 characters long and contain at least one letter and one number.', ]; return $messages[$error] ?? 'An error occurred.'; } public function destroy(Request $request, string $id): Response { $currentUser = $this->getCurrentUser(); $query = User::where('users_id', $id); if (!$currentUser->isSuperAdmin()) { $query->where('users_bus_id', $currentUser->users_bus_id); } $user = $query->first(); // Prevent deleting self and check if user exists/accessible if ($user && $user->users_id != $_SESSION['user_id']) { if ($user->users_avatar) { $uploadDir = dirname(__DIR__, 4) . '/storage/uploads/avatars'; if (file_exists($uploadDir . '/' . $user->users_avatar)) { unlink($uploadDir . '/' . $user->users_avatar); } } $user->delete(); } return new RedirectResponse(url('admin/users')); } public function manual(Request $request): Response { return $this->renderAdminView('users/views/admin/manual.twig', [ 'title' => 'Manual do Usuário - Usuários e Permissões' ]); } }