roleRepository->list(); } public function listActive(): array { return $this->roleRepository->listActive(); } public function listPaged(array $options): array { return $this->roleRepository->listPaged($options); } public function findByUuid(string $uuid): ?array { return $this->roleRepository->findByUuid($uuid); } public function findById(int $id): ?array { return $this->roleRepository->find($id); } public function createFromAdmin(array $input, int $currentUserId = 0): array { $form = $this->sanitizeBase($input); $errors = $this->validateBase($form); $warnings = $this->warningsForCode($form, 0); if ($errors) { return ['ok' => false, 'errors' => $errors, 'warnings' => $warnings, 'form' => $form]; } $createdId = $this->roleRepository->create([ 'description' => $form['description'], 'code' => $form['code'] ?: null, 'active' => $form['active'], 'created_by' => $currentUserId > 0 ? $currentUserId : null, ]); if (!$createdId) { return ['ok' => false, 'errors' => [t('Role can not be created')], 'form' => $form]; } $createdRole = $this->roleRepository->find((int) $createdId); $uuid = $createdRole['uuid'] ?? null; if (!empty($input['is_default'])) { $this->settingsGateway->setDefaultRoleId((int) $createdId); } $this->systemAuditService->record('admin.roles.create', 'success', [ 'actor_user_id' => $currentUserId > 0 ? $currentUserId : null, 'target_type' => 'role', 'target_id' => (int) $createdId, 'target_uuid' => is_string($uuid) ? $uuid : '', 'metadata' => ['active' => $form['active']], ]); return ['ok' => true, 'form' => $form, 'warnings' => $warnings, 'uuid' => $uuid, 'id' => (int) $createdId]; } public function updateFromAdmin(int $roleId, array $input, int $currentUserId = 0): array { $form = $this->sanitizeBase($input); $errors = $this->validateBase($form); $warnings = $this->warningsForCode($form, $roleId); if ($errors) { return ['ok' => false, 'errors' => $errors, 'warnings' => $warnings, 'form' => $form]; } $existingBefore = $this->roleRepository->find($roleId) ?? []; $updated = $this->roleRepository->update($roleId, [ 'description' => $form['description'], 'code' => $form['code'] ?: null, 'active' => $form['active'], 'modified_by' => $currentUserId > 0 ? $currentUserId : null, ]); if (!$updated) { return ['ok' => false, 'errors' => [t('Role can not be updated')], 'form' => $form]; } $this->systemAuditService->record('admin.roles.update', 'success', [ 'actor_user_id' => $currentUserId > 0 ? $currentUserId : null, 'target_type' => 'role', 'target_id' => $roleId, 'target_uuid' => (string) ($existingBefore['uuid'] ?? ''), 'before' => ['active' => $existingBefore['active'] ?? null], 'after' => ['active' => $form['active']], ]); return ['ok' => true, 'form' => $form, 'warnings' => $warnings]; } public function deleteByUuid(string $uuid): array { $uuid = trim($uuid); if ($uuid === '') { return ['ok' => false, 'status' => 404, 'error' => 'not_found']; } $role = $this->roleRepository->findByUuid($uuid); if (!$role || !isset($role['id'])) { return ['ok' => false, 'status' => 404, 'error' => 'not_found']; } if (($role['description'] ?? '') === 'Admin') { return ['ok' => false, 'status' => 403, 'error' => 'admin_role_protected']; } $deleted = $this->roleRepository->delete((int) $role['id']); if (!$deleted) { return ['ok' => false, 'status' => 500, 'error' => 'delete_failed']; } $this->systemAuditService->record('admin.roles.delete', 'success', [ 'target_type' => 'role', 'target_id' => (int) $role['id'], 'target_uuid' => (string) ($role['uuid'] ?? ''), ]); return ['ok' => true, 'role' => $role]; } private function sanitizeBase(array $input): array { return [ 'description' => trim((string) ($input['description'] ?? '')), 'code' => trim((string) ($input['code'] ?? '')), 'active' => $this->normalizeActive($input['active'] ?? 1), ]; } private function validateBase(array $form): array { $errors = []; if ($form['description'] === '') { $errors[] = t('Description cannot be empty'); } return $errors; } // Duplicate codes are a warning, not a hard error — codes are optional identifiers, // not unique keys, so duplicates are allowed but surfaced to the user. private function warningsForCode(array $form, int $excludeId): array { $warnings = []; $code = $form['code'] ?? ''; if ($code !== '' && $this->roleRepository->existsByCode($code, $excludeId)) { $warnings[] = t('Role code already exists'); } return $warnings; } private function normalizeActive($value): int { $value = strtolower(trim((string) $value)); if (in_array($value, ['0', 'false', 'inactive'], true)) { return 0; } return 1; } }