Compare commits
No commits in common. "9d394c34f4282d5a159c8bf312c8f80809ffc3a7" and "d884ff4155306bafdf2328c469e89b44a977aca7" have entirely different histories.
9d394c34f4
...
d884ff4155
|
|
@ -34,9 +34,10 @@ export default class extends Controller {
|
||||||
async connectToMercure() {
|
async connectToMercure() {
|
||||||
try {
|
try {
|
||||||
// Fetch the JWT token and topic from the server
|
// Fetch the JWT token and topic from the server
|
||||||
const response = await fetch('/mercure-token');
|
const response = await fetch('/notifications/mercure-token');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
|
console.log('Mercure token data:', data);
|
||||||
|
|
||||||
// Use server-provided topic if available, otherwise fallback to default per-user topic
|
// Use server-provided topic if available, otherwise fallback to default per-user topic
|
||||||
const topic = data.topic || `http://portail.solutions-easy.moi/notifications/user/${this.userIdValue}`;
|
const topic = data.topic || `http://portail.solutions-easy.moi/notifications/user/${this.userIdValue}`;
|
||||||
|
|
@ -48,6 +49,11 @@ export default class extends Controller {
|
||||||
url.searchParams.append('authorization', data.token);
|
url.searchParams.append('authorization', data.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('Connecting to Mercure...');
|
||||||
|
console.log('Mercure URL:', this.mercureUrlValue);
|
||||||
|
console.log('Topic:', topic);
|
||||||
|
console.log('Full URL:', url.toString());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.eventSource = new EventSource(url.toString());
|
this.eventSource = new EventSource(url.toString());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -56,9 +62,11 @@ export default class extends Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.eventSource.onopen = () => {
|
this.eventSource.onopen = () => {
|
||||||
|
console.log('✅ Mercure connection established successfully!');
|
||||||
};
|
};
|
||||||
|
|
||||||
this.eventSource.onmessage = (event) => {
|
this.eventSource.onmessage = (event) => {
|
||||||
|
console.log('📨 New notification received:', event.data);
|
||||||
try {
|
try {
|
||||||
const notification = JSON.parse(event.data);
|
const notification = JSON.parse(event.data);
|
||||||
this.handleNewNotification(notification);
|
this.handleNewNotification(notification);
|
||||||
|
|
@ -68,6 +76,7 @@ export default class extends Controller {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.eventSource.onerror = (error) => {
|
this.eventSource.onerror = (error) => {
|
||||||
|
console.error('❌ Mercure connection error:', error);
|
||||||
try {
|
try {
|
||||||
console.error('EventSource readyState:', this.eventSource.readyState);
|
console.error('EventSource readyState:', this.eventSource.readyState);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Controller;
|
|
||||||
|
|
||||||
use App\Service\UserService;
|
|
||||||
use Lcobucci\JWT\Configuration;
|
|
||||||
use Lcobucci\JWT\Signer\Hmac\Sha256;
|
|
||||||
use Lcobucci\JWT\Signer\Key\InMemory;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
|
||||||
|
|
||||||
class MercureController extends AbstractController
|
|
||||||
{
|
|
||||||
public function __construct(private readonly UserService $userService)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#[Route(path: '/mercure-token', name: 'mercure_token', methods: ['GET'])]
|
|
||||||
public function getMercureToken(): JsonResponse
|
|
||||||
{
|
|
||||||
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
|
|
||||||
$user = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
|
||||||
|
|
||||||
$topic = sprintf('http://portail.solutions-easy.moi/notifications/user/%d', $user->getId());
|
|
||||||
|
|
||||||
// Generate JWT token for Mercure subscription
|
|
||||||
$secret = $_ENV['MERCURE_JWT_SECRET'];
|
|
||||||
|
|
||||||
$config = Configuration::forSymmetricSigner(
|
|
||||||
new Sha256(),
|
|
||||||
InMemory::plainText($secret)
|
|
||||||
);
|
|
||||||
|
|
||||||
$token = $config->builder()
|
|
||||||
->withClaim('mercure', [
|
|
||||||
'subscribe' => [$topic]
|
|
||||||
])
|
|
||||||
->getToken($config->signer(), $config->signingKey());
|
|
||||||
|
|
||||||
return new JsonResponse([
|
|
||||||
'token' => $token->toString(),
|
|
||||||
'topic' => $topic,
|
|
||||||
'userId' => $user->getId(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -113,5 +113,32 @@ class NotificationController extends AbstractController
|
||||||
return new JsonResponse(['success' => true]);
|
return new JsonResponse(['success' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route(path: '/mercure-token', name: 'mercure_token', methods: ['GET'])]
|
||||||
|
public function getMercureToken(): JsonResponse
|
||||||
|
{
|
||||||
|
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
|
||||||
|
$user = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
||||||
|
|
||||||
|
$topic = sprintf('http://portail.solutions-easy.moi/notifications/user/%d', $user->getId());
|
||||||
|
|
||||||
|
// Generate JWT token for Mercure subscription
|
||||||
|
$secret = $_ENV['MERCURE_JWT_SECRET'];
|
||||||
|
|
||||||
|
$config = Configuration::forSymmetricSigner(
|
||||||
|
new Sha256(),
|
||||||
|
InMemory::plainText($secret)
|
||||||
|
);
|
||||||
|
|
||||||
|
$token = $config->builder()
|
||||||
|
->withClaim('mercure', [
|
||||||
|
'subscribe' => [$topic]
|
||||||
|
])
|
||||||
|
->getToken($config->signer(), $config->signingKey());
|
||||||
|
|
||||||
|
return new JsonResponse([
|
||||||
|
'token' => $token->toString(),
|
||||||
|
'topic' => $topic,
|
||||||
|
'userId' => $user->getId(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,62 +57,68 @@ class UserController extends AbstractController
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO : REMOVE DEAD CODE due to the use of tabulator in the frontend
|
||||||
#[Route('/view/{id}', name: 'show', methods: ['GET'])]
|
#[Route('/view/{id}', name: 'show', methods: ['GET'])]
|
||||||
public function view(int $id, Request $request): Response
|
public function view(int $id, Request $request): Response
|
||||||
{
|
{
|
||||||
// Accès : uniquement utilisateur authentifié
|
|
||||||
$this->denyAccessUnlessGranted('ROLE_USER');
|
$this->denyAccessUnlessGranted('ROLE_USER');
|
||||||
|
|
||||||
// Utilisateur courant (acting user) via UserService
|
|
||||||
$actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
$actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
||||||
|
|
||||||
// Vérification des droits d'accès supplémentaires
|
|
||||||
if (!$this->userService->hasAccessTo($actingUser)) {
|
if (!$this->userService->hasAccessTo($actingUser)) {
|
||||||
throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chargement de l'utilisateur cible à afficher
|
|
||||||
$user = $this->userRepository->find($id);
|
$user = $this->userRepository->find($id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Paramètre optionnel de contexte organisationnel
|
|
||||||
$orgId = $request->query->get('organizationId');
|
$orgId = $request->query->get('organizationId');
|
||||||
|
|
||||||
// Liste de toutes les applications (pour créer des groupes même si vides)
|
|
||||||
$apps = $this->appsRepository->findAll();
|
$apps = $this->appsRepository->findAll();
|
||||||
|
$roles = $this->rolesRepository->findAll();
|
||||||
|
|
||||||
// Initialisations pour la résolution des UsersOrganizations (UO)
|
$data = [
|
||||||
|
'roles' => $roles,
|
||||||
|
];
|
||||||
|
|
||||||
|
$uoList = [];
|
||||||
$singleUo = null;
|
$singleUo = null;
|
||||||
$uoActive = null;
|
$uoActive = null;
|
||||||
|
$orgs = [];
|
||||||
|
|
||||||
// get uo or uoS based on orgId
|
|
||||||
if ($orgId) {
|
if ($orgId) {
|
||||||
// Contexte organisation précis : récupérer l'organisation et les liens UO
|
// Specific organization context
|
||||||
$organization = $this->organizationRepository->findBy(['id' => $orgId]);
|
$orgs = $this->organizationRepository->findBy(['id' => $orgId]);
|
||||||
$uoList = $this->uoRepository->findBy([
|
$uoList = $this->uoRepository->findBy([
|
||||||
'users' => $user,
|
'users' => $user,
|
||||||
'organization' => $organization,
|
'organization' => $orgs,
|
||||||
'isActive' => true,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!$uoList) {
|
if (!$uoList) {
|
||||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si contexte org donné, on retient la première UO (singleUo)
|
|
||||||
$singleUo = $uoList[0];
|
$singleUo = $uoList[0];
|
||||||
$data["singleUo"] = $singleUo;
|
|
||||||
$uoActive = $singleUo->isActive();
|
$uoActive = $singleUo->isActive();
|
||||||
} else {
|
} else {
|
||||||
// Pas de contexte org : récupérer toutes les UO actives de l'utilisateur
|
// All active organizations
|
||||||
$uoList = $this->uoRepository->findBy([
|
$uoList = $this->uoRepository->findBy([
|
||||||
'users' => $user,
|
'users' => $user,
|
||||||
'isActive' => true,
|
'isActive'=> true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
foreach ($uoList as $u) {
|
||||||
|
$orgs[] = $u->getOrganization();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Charger les liens UserOrganizationApp (UOA) actifs pour les UO trouvées
|
if (count($uoList) === 1) {
|
||||||
|
$singleUo = $uoList[0];
|
||||||
|
$uoActive = $singleUo->isActive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['uoList'] = $uoList;
|
||||||
|
$data['singleUo'] = $singleUo;
|
||||||
|
|
||||||
// Load user-organization-app roles (can be empty)
|
// Load user-organization-app roles (can be empty)
|
||||||
$uoa = $this->entityManager
|
$uoa = $this->entityManager
|
||||||
->getRepository(UserOrganizatonApp::class)
|
->getRepository(UserOrganizatonApp::class)
|
||||||
|
|
@ -121,41 +127,80 @@ class UserController extends AbstractController
|
||||||
'isActive' => true,
|
'isActive' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Group UOA by app and ensure every app has a group
|
// Group existing UOA per app
|
||||||
$data['uoas'] = $this->userOrganizationAppService
|
$uoas = $this->userOrganizationAppService
|
||||||
->groupUserOrganizationAppsByApplication(
|
->groupUserOrganizationAppsByApplication($uoa);
|
||||||
$uoa,
|
|
||||||
$apps,
|
|
||||||
$singleUo ? $singleUo->getId() : null
|
|
||||||
);
|
|
||||||
|
|
||||||
//Build roles based on user permissions.
|
// ---------- HERE: create empty groups for apps with no UOA ----------
|
||||||
//Admin can't see or edit a super admin user
|
// Index existing groups by app id
|
||||||
if ($this->isGranted('ROLE_SUPER_ADMIN')) {
|
$indexedUoas = [];
|
||||||
$data['rolesArray'] = $this->rolesRepository->findAll();
|
foreach ($uoas as $group) {
|
||||||
} elseif (!$orgId) {
|
$indexedUoas[$group['application']->getId()] = $group;
|
||||||
$data['rolesArray'] = $this->userService->getRolesArrayForUser($actingUser, true);
|
|
||||||
} else {
|
|
||||||
$data['rolesArray'] = $this->userService->getRolesArrayForUser($actingUser);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load all possible roles once
|
||||||
|
$allRoles = $this->entityManager->getRepository(Roles::class)->findAll();
|
||||||
|
|
||||||
|
foreach ($apps as $app) {
|
||||||
|
$appId = $app->getId();
|
||||||
|
|
||||||
|
if (!isset($indexedUoas[$appId])) {
|
||||||
|
// No UOA for this app yet: create an empty group
|
||||||
|
$indexedUoas[$appId] = [
|
||||||
|
'uoId' => $singleUo ? $singleUo->getId() : null,
|
||||||
|
'application' => $app,
|
||||||
|
'roles' => [],
|
||||||
|
'rolesArray' => [],
|
||||||
|
'selectedRoleIds' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($allRoles as $role) {
|
||||||
|
// Same security logic: ADMIN cannot assign SUPER ADMIN
|
||||||
|
if ($this->isGranted('ROLE_ADMIN')
|
||||||
|
&& !$this->isGranted('ROLE_SUPER_ADMIN')
|
||||||
|
&& $role->getName() === 'SUPER ADMIN') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$indexedUoas[$appId]['rolesArray'][] = [
|
||||||
|
'id' => $role->getId(),
|
||||||
|
'name' => $role->getName(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!$orgId){
|
||||||
|
$data['singleUo'] = null;
|
||||||
|
}
|
||||||
|
// Overwrite $uoas to include groups for *all* apps
|
||||||
|
$uoas = array_values($indexedUoas);
|
||||||
|
$data['uoas'] = $uoas;
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
// Calcul du flag de modification : utilisateur admin ET exactement 1 UO
|
// Compute "can edit" flag: admin AND exactly one UO
|
||||||
$canEdit = $this->userService->canEditRolesCheck($actingUser, $user, $organization, $this->isGranted('ROLE_ADMIN'));
|
$canEditRoles = $this->isGranted('ROLE_ADMIN') && count($uoList) === 1;
|
||||||
|
|
||||||
|
$this->actionService->createAction(
|
||||||
|
"View user information",
|
||||||
|
$actingUser,
|
||||||
|
null,
|
||||||
|
$user->getUserIdentifier()
|
||||||
|
);
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// En cas d'erreur, désactiver l'édition et logger l'exception
|
$canEditRoles = false;
|
||||||
$canEdit = false;
|
|
||||||
$this->logger->error($e->getMessage());
|
$this->logger->error($e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render('user/show.html.twig', [
|
return $this->render('user/show.html.twig', [
|
||||||
'user' => $user,
|
'user' => $user,
|
||||||
|
'uoas' => $uoas ?? null,
|
||||||
|
'orgs' => $orgs ?? null,
|
||||||
'organizationId' => $orgId ?? null,
|
'organizationId' => $orgId ?? null,
|
||||||
'uoActive' => $uoActive ?? null,
|
'uoActive' => $uoActive ?? null,
|
||||||
'apps' => $apps ?? [],
|
'apps' => $apps ?? [],
|
||||||
'data' => $data ?? [],
|
'data' => $data ?? [],
|
||||||
'canEdit' => $canEdit ?? false,
|
'canEditRoles' => $canEditRoles ?? false,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -465,9 +510,9 @@ class UserController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($selectedRolesIds)) {
|
if (!empty($selectedRolesIds)) {
|
||||||
if (!in_array((string)$roleUser->getId(), $selectedRolesIds, true)) {
|
if (!in_array((string)$roleUser->getId(), $selectedRolesIds, true)){
|
||||||
$this->userOrganizationAppService->deactivateAllUserOrganizationsAppLinks($uo, $application);
|
$this->userOrganizationAppService->deactivateAllUserOrganizationsAppLinks($uo, $application);
|
||||||
} else {
|
}else{
|
||||||
$this->userOrganizationAppService->syncRolesForUserOrganizationApp(
|
$this->userOrganizationAppService->syncRolesForUserOrganizationApp(
|
||||||
$uo,
|
$uo,
|
||||||
$application,
|
$application,
|
||||||
|
|
|
||||||
|
|
@ -19,21 +19,18 @@ class UserOrganizationAppService
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Groups UserOrganizationApp by application and ensures every app has a group (even if empty).
|
* Groups UserOrganizationApp entities by Application
|
||||||
|
* and prepares data for Twig.
|
||||||
*
|
*
|
||||||
* @param array $userOrgApps Array of UserOrganizatonApp entities
|
* @param UserOrganizatonApp[] $userOrgApps
|
||||||
* @param array $allApps Array of all Application entities
|
* @return array
|
||||||
* @param int|null $defaultUoId The UserOrganization ID to use for apps with no UOA
|
|
||||||
* @return array Indexed by app ID: ['uoId' => int|null, 'application' => App, 'selectedRoleIds' => int[]]
|
|
||||||
*/
|
*/
|
||||||
public function groupUserOrganizationAppsByApplication(
|
public function groupUserOrganizationAppsByApplication(array $userOrgApps): array
|
||||||
array $userOrgApps,
|
{
|
||||||
array $allApps,
|
|
||||||
?int $defaultUoId = null
|
|
||||||
): array {
|
|
||||||
$grouped = [];
|
$grouped = [];
|
||||||
|
|
||||||
foreach ($userOrgApps as $uoa) {
|
foreach ($userOrgApps as $uoa) {
|
||||||
|
|
||||||
$app = $uoa->getApplication();
|
$app = $uoa->getApplication();
|
||||||
$appId = $app->getId();
|
$appId = $app->getId();
|
||||||
$roleEntity = $uoa->getRole();
|
$roleEntity = $uoa->getRole();
|
||||||
|
|
@ -42,27 +39,39 @@ class UserOrganizationAppService
|
||||||
$grouped[$appId] = [
|
$grouped[$appId] = [
|
||||||
'uoId' => $uoa->getUserOrganization()->getId(),
|
'uoId' => $uoa->getUserOrganization()->getId(),
|
||||||
'application' => $app,
|
'application' => $app,
|
||||||
|
'roles' => [],
|
||||||
|
'rolesArray' => [],
|
||||||
'selectedRoleIds' => [],
|
'selectedRoleIds' => [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$grouped[$appId]['roles'][] = [
|
||||||
|
'id' => $roleEntity->getId(),
|
||||||
|
'name' => $roleEntity->getName(),
|
||||||
|
];
|
||||||
$grouped[$appId]['selectedRoleIds'][] = $roleEntity->getId();
|
$grouped[$appId]['selectedRoleIds'][] = $roleEntity->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure every app has a group
|
// Load all possible roles once
|
||||||
foreach ($allApps as $app) {
|
$allRoles = $this->entityManager->getRepository(Roles::class)->findAll();
|
||||||
$appId = $app->getId();
|
|
||||||
|
|
||||||
if (!isset($grouped[$appId])) {
|
foreach ($grouped as &$appGroup) {
|
||||||
$grouped[$appId] = [
|
foreach ($allRoles as $role) {
|
||||||
'uoId' => $defaultUoId,
|
// exclude SUPER ADMIN from assignable roles if current user is just ADMIN
|
||||||
'application' => $app,
|
if ($this->security->isGranted('ROLE_ADMIN')
|
||||||
'selectedRoleIds' => [],
|
&& !$this->security->isGranted('ROLE_SUPER_ADMIN')
|
||||||
|
&& $role->getName() === 'SUPER ADMIN') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$appGroup['rolesArray'][] = [
|
||||||
|
'id' => $role->getId(),
|
||||||
|
'name' => $role->getName(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $grouped; // IMPORTANT: keep indexed by appId
|
return array_values($grouped);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,21 @@ class UserService
|
||||||
throw new FileException('File upload failed: ' . $e->getMessage());
|
throw new FileException('File upload failed: ' . $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// // Define upload directory
|
||||||
|
// $uploadDirectory = $this->profileDirectory;
|
||||||
|
// // Create directory if it doesn't exist
|
||||||
|
// if (!is_dir($uploadDirectory) && !mkdir($uploadDirectory, 0755, true) && !is_dir($uploadDirectory)) {
|
||||||
|
// throw new DirectoryCouldNotBeCreatedException(sprintf('Directory "%s" was not created', $uploadDirectory));
|
||||||
|
// }
|
||||||
|
// try {
|
||||||
|
//
|
||||||
|
// // Move the file to the upload directory
|
||||||
|
// $picture->move($uploadDirectory, $customFilename);
|
||||||
|
//
|
||||||
|
// // Update user entity with the file path (relative to public directory)
|
||||||
|
// $user->setPictureUrl('uploads/profile/' . $customFilename);
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
@ -456,41 +471,4 @@ class UserService
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get roles array for a user, optionally including super admin roles.
|
|
||||||
* ViewSAdminRoles flag determines if super admin roles should be included.
|
|
||||||
*
|
|
||||||
* @param User $actingUser
|
|
||||||
* @param bool $viewSAdminRoles
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getRolesArrayForUser(User $actingUser, bool $viewSAdminRoles = false): array
|
|
||||||
{
|
|
||||||
$roles = $this->entityManager->getRepository(Roles::class)->findAll();
|
|
||||||
$rolesArray = [];
|
|
||||||
foreach ($roles as $role) {
|
|
||||||
if (!$viewSAdminRoles && $role->getName() === 'SUPER ADMIN') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$rolesArray[] = [
|
|
||||||
'id' => $role->getId(),
|
|
||||||
'name' => $role->getName(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $rolesArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function canEditRolesCheck(User $actingUser, User $user, $org, bool $isAdmin): bool
|
|
||||||
{
|
|
||||||
$userRoles = $user->getRoles();
|
|
||||||
$actingUserRoles = $actingUser->getRoles();
|
|
||||||
// if acting user is admin, he can´t edit super admin roles
|
|
||||||
|
|
||||||
if (in_array('ROLE_SUPER_ADMIN', $userRoles, true) && !in_array('ROLE_SUPER_ADMIN', $actingUserRoles, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return $isAdmin && !empty($org);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,15 @@
|
||||||
|
|
||||||
<div class="card border-0 no-header-bg ">
|
<div class="card border-0 no-header-bg ">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
|
{% if orgs|length >0 %}
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h1>Vos applications</h1>
|
<h1>Vos applications</h1>
|
||||||
</div>
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="card-title">
|
||||||
|
<h1>Aucune application</h1>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -56,23 +62,30 @@
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<p>
|
<p><b>Description
|
||||||
<b>Description :</b>
|
:</b> {{ app.descriptionSmall|default('Aucune description disponible.')|raw }}
|
||||||
{{ app.descriptionSmall|default('Aucune description disponible.')|raw }}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
{# EDITABLE if admin and exactly one UO #}
|
||||||
{# find appGroup once, used in both editable and read-only branches #}
|
{% if canEditRoles and data.singleUo is not null %}
|
||||||
{% set appGroup = data.uoas[app.id]|default(null) %}
|
|
||||||
|
|
||||||
{% if canEdit %}
|
|
||||||
<form method="POST"
|
<form method="POST"
|
||||||
action="{{ path('user_application_role', { id: data.singleUo.id }) }}">
|
action="{{ path('user_application_role', { id: data.singleUo.id }) }}">
|
||||||
|
{# for this app, find its grouped info #}
|
||||||
|
{# Find the group for this specific app #}
|
||||||
|
{% set appGroup = null %}
|
||||||
|
{% for group in data.uoas|default([]) %}
|
||||||
|
{% if group.application.id == app.id %}
|
||||||
|
{% set appGroup = group %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
<div class="form-group mb-3">
|
<div class="form-group mb-3">
|
||||||
<label for="roles-{{ app.id }}"><b>Rôles :</b></label>
|
<label for="roles-{{ app.id }}"><b>Rôles :</b></label>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
|
|
||||||
{% if appGroup %}
|
{% if appGroup %}
|
||||||
{% for role in data.rolesArray %}
|
{# Use rolesArray: filtered by current user's level (no SUPER ADMIN for plain ADMIN, etc.) #}
|
||||||
|
{% for role in appGroup.rolesArray %}
|
||||||
<input class="form-check-input" type="checkbox"
|
<input class="form-check-input" type="checkbox"
|
||||||
name="roles[]"
|
name="roles[]"
|
||||||
value="{{ role.id }}"
|
value="{{ role.id }}"
|
||||||
|
|
@ -87,8 +100,12 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</label>
|
</label>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<p class="text-muted">Aucun rôle défini pour cette application.</p>
|
|
||||||
|
<p class="text-muted">Aucun rôle défini pour cette
|
||||||
|
application.</p>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" name="appId" value="{{ app.id }}"
|
<button type="submit" name="appId" value="{{ app.id }}"
|
||||||
|
|
@ -97,30 +114,39 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
{# READ ONLY otherwise #}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
{% set appGroup = null %}
|
||||||
|
{% for group in data.uoas|default([]) %}
|
||||||
|
{% if group.application.id == app.id %}
|
||||||
|
{% set appGroup = group %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
<div class="form-group mb-3">
|
<div class="form-group mb-3">
|
||||||
<label for="roles-{{ app.id }}"><b>Rôles :</b></label>
|
<label for="roles-{{ app.id }}"><b>Rôles :</b></label>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
|
|
||||||
{% if appGroup %}
|
{% if appGroup %}
|
||||||
{% for role in data.rolesArray %}
|
{# Use rolesArray: filtered by current user's level (no SUPER ADMIN for plain ADMIN, etc.) #}
|
||||||
|
{% for role in appGroup.rolesArray %}
|
||||||
<input class="form-check-input" type="checkbox"
|
<input class="form-check-input" type="checkbox"
|
||||||
disabled
|
disabled
|
||||||
name="roles[]"
|
name="roles[]"
|
||||||
value="{{ role.id }}"
|
value="{{ role.id }}"
|
||||||
id="role-{{ role.id }}-app-{{ app.id }}"
|
id="role-{{ role.id }}-app-{{ app.id }}"
|
||||||
{% if role.id in appGroup.selectedRoleIds %}checked{% endif %}>
|
{% if appGroup and role.id in appGroup.selectedRoleIds %}checked{% endif %}>
|
||||||
<label class="form-check"
|
<label class="form-check"
|
||||||
for="role-{{ role.id }}-app-{{ app.id }}">
|
for="role-{{ role.id }}-app-{{ app.id }}">
|
||||||
{% if role.name == 'USER' %}
|
{{ role.name }}
|
||||||
Accès
|
|
||||||
{% else %}
|
|
||||||
{{ role.name|capitalize }}
|
|
||||||
{% endif %}
|
|
||||||
</label>
|
</label>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<p class="text-muted">Aucun rôle défini pour cette application.</p>
|
<p class="text-muted">Aucun rôle défini pour cette
|
||||||
|
application.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
{% if canEdit %}
|
|
||||||
{% if organizationId is not null %}
|
{% if organizationId is not null %}
|
||||||
{% if uoActive %}
|
{% if uoActive %}
|
||||||
<form method="post" action="{{ path('user_deactivate_organization', {'id': user.id}) }}"
|
<form method="post" action="{{ path('user_deactivate_organization', {'id': user.id}) }}"
|
||||||
onsubmit="return confirm('Vous allez retirer l\'utilisateur de cette organisation, êtes vous sûre?');">
|
onsubmit="return confirm('Vous allez retirer l\'utilisateur de cette organisation, êtes vous sûre?');">
|
||||||
<input type="hidden" name="organizationId" value="{{ organizationId }}">
|
<input type="hidden" name="organizationId" value="{{ organizationId }}">
|
||||||
<button class="btn btn-secondary" type="submit">Désactiver l'utilisateur de
|
<button class="btn btn-secondary" type="submit">Désactiver l'utilisateur de l'organisation
|
||||||
l'organisation
|
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
@ -31,11 +29,8 @@
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<a href="{{ path('user_edit', {'id': user.id, 'organizationId': organizationId}) }}"
|
<a href="{{ path('user_edit', {'id': user.id, 'organizationId': organizationId}) }}"
|
||||||
class="btn btn-primary">Modifier</a>
|
class="btn btn-primary">Modifier</a>
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue