['html']]), new TwigFunction('get_current_org', [$this, 'getCurrentOrg'], ['is_safe' => ['html']]), new TwigFunction('get_alerts', [$this, 'getAlerts'], ['is_safe' => ['html']]), new TwigFunction('render_pager', [$this, 'renderPager'], ['is_safe' => ['html']]), new TwigFunction('is_active_route', [$this, 'isActiveRoute'], ['is_safe' => ['html']]), new TwigFunction('get_current_route', [$this, 'getCurrentRoute'], ['is_safe' => ['html']]), new TwigFunction('render_actions', [$this, 'renderActions'], ['is_safe' => ['html']]), new TwigFunction('render_cell', [$this, 'renderCell'], ['is_safe' => ['html']]), // Access functions new TwigFunction('can', [$this, 'can'], ['is_safe' => ['html']]), new TwigFunction('is_role', [$this, 'isRole'], ['is_safe' => ['html']]), new TwigFunction('is_owner', [$this, 'isOwner'], ['is_safe' => ['html']]), new TwigFunction('is_admin', [$this, 'isAdmin'], ['is_safe' => ['html']]), new TwigFunction('is_manager', [$this, 'isManager'], ['is_safe' => ['html']]), new TwigFunction('current_role', [$this, 'currentRole'], ['is_safe' => ['html']]), new TwigFunction('role_label', [$this, 'roleLabel'], ['is_safe' => ['html']]), new TwigFunction('can_view', [$this, 'canView'], ['is_safe' => ['html']]), new TwigFunction('can_create', [$this, 'canCreate'], ['is_safe' => ['html']]), new TwigFunction('can_edit', [$this, 'canEdit'], ['is_safe' => ['html']]), new TwigFunction('can_delete', [$this, 'canDelete'], ['is_safe' => ['html']]), new TwigFunction('can_manage_users', [$this, 'canManageUsers'], ['is_safe' => ['html']]), // Role & Status badge functions new TwigFunction('role_badge', [$this, 'roleBadge'], ['is_safe' => ['html']]), new TwigFunction('status_badge', [$this, 'statusBadge'], ['is_safe' => ['html']]), new TwigFunction('get_all_roles', [$this, 'getAllRoles'], ['is_safe' => ['html']]), ]; } // ======================================== // Access Functions для Twig // ======================================== public function can(string $action, string $resource): bool { return service('access')->can($action, $resource); } public function isRole($roles): bool { return service('access')->isRole($roles); } public function isOwner(): bool { return service('access')->isRole(\App\Services\AccessService::ROLE_OWNER); } public function isAdmin(): bool { $role = service('access')->getCurrentRole(); return $role === \App\Services\AccessService::ROLE_ADMIN || $role === \App\Services\AccessService::ROLE_OWNER; } public function isManager(): bool { return service('access')->isManagerOrHigher(); } public function currentRole(): ?string { return service('access')->getCurrentRole(); } public function roleLabel(string $role): string { return service('access')->getRoleLabel($role); } public function canView(string $resource): bool { return service('access')->can(\App\Services\AccessService::PERMISSION_VIEW, $resource); } public function canCreate(string $resource): bool { return service('access')->can(\App\Services\AccessService::PERMISSION_CREATE, $resource); } public function canEdit(string $resource): bool { return service('access')->can(\App\Services\AccessService::PERMISSION_EDIT, $resource); } public function canDelete(string $resource): bool { return service('access')->can(\App\Services\AccessService::PERMISSION_DELETE, $resource); } public function canManageUsers(): bool { return service('access')->canManageUsers(); } // ======================================== // Role & Status Badge Functions // ======================================== public function roleBadge(string $role): string { $colors = [ 'owner' => 'bg-primary', 'admin' => 'bg-info', 'manager' => 'bg-success', 'guest' => 'bg-secondary', ]; $labels = [ 'owner' => 'Владелец', 'admin' => 'Администратор', 'manager' => 'Менеджер', 'guest' => 'Гость', ]; $color = $colors[$role] ?? 'bg-secondary'; $label = $labels[$role] ?? $role; return '' . esc($label) . ''; } public function statusBadge(string $status): string { $colors = [ 'active' => 'bg-success', 'pending' => 'bg-warning text-dark', 'blocked' => 'bg-danger', ]; $labels = [ 'active' => 'Активен', 'pending' => 'Ожидает', 'blocked' => 'Заблокирован', ]; $color = $colors[$status] ?? 'bg-secondary'; $label = $labels[$status] ?? $status; return '' . esc($label) . ''; } public function getAllRoles(): array { return \App\Services\AccessService::getAllRoles(); } public function getSession() { return session(); } public function getCurrentOrg() { $session = session(); $activeOrgId = $session->get('active_org_id'); if ($activeOrgId) { $orgModel = new OrganizationModel(); return $orgModel->find($activeOrgId); } return null; } public function getAlerts(): array { $session = session(); $alerts = []; $types = ['success', 'error', 'warning', 'info']; foreach ($types as $type) { if ($msg = $session->getFlashdata($type)) { $alerts[] = ['type' => $type, 'message' => $msg]; } } if ($validationErrors = $session->getFlashdata('errors')) { foreach ($validationErrors as $error) { $alerts[] = ['type' => 'error', 'message' => $error]; } } return $alerts; } public function renderPager($pager) { if (!$pager) { return ''; } return $pager->links(); } /** * Проверяет, является ли текущий маршрут активным */ public function isActiveRoute($routes, $exact = false): bool { $currentRoute = $this->getCurrentRoute(); if (is_string($routes)) { $routes = [$routes]; } foreach ($routes as $route) { if ($exact) { // Точное совпадение if ($currentRoute === $route) { return true; } } else { // Частичное совпадение (начинается с) // Исключаем пустую строку, так как она совпадает с любым маршрутом if ($route === '') { // Для пустого маршрута проверяем только корень if ($currentRoute === '') { return true; } } elseif (strpos($currentRoute, $route) === 0) { return true; } } } return false; } /** * Получает текущий маршрут без базового URL */ public function getCurrentRoute(): string { $uri = service('uri'); $route = $uri->getRoutePath(); // Убираем начальный слеш если есть return ltrim($route, '/'); } /** * Генерирует HTML для кнопок действий в строке таблицы * * @param object|array $item Данные строки (объект или массив) * @param array $actions Массив конфигураций действий * @return string HTML код кнопок */ public function renderActions($item, array $actions = []): string { if (empty($actions)) { return ''; } // Конвертируем объект в массив для доступа к свойствам $itemArray = $this->objectToArray($item); // DEBUG: логируем для отладки log_message('debug', 'renderActions: item type = ' . gettype($item)); log_message('debug', 'renderActions: item keys = ' . (is_array($itemArray) ? implode(', ', array_keys($itemArray)) : 'N/A')); $html = '