display users for super admin
This commit is contained in:
parent
8a19b01893
commit
3ca1446b91
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Organizations;
|
||||
use App\Entity\User;
|
||||
use App\Entity\UsersOrganizations;
|
||||
use App\Form\UserForm;
|
||||
use App\Service\ActionService;
|
||||
use App\Service\UserOrganizationService;
|
||||
|
|
@ -25,7 +27,8 @@ class UserController extends AbstractController
|
|||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly UserService $userService,
|
||||
private readonly ActionService $actionService,
|
||||
) {
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
#[Route('/', name: 'index', methods: ['GET'])]
|
||||
|
|
@ -33,14 +36,20 @@ class UserController extends AbstractController
|
|||
{
|
||||
$this->denyAccessUnlessGranted('ROLE_USER');
|
||||
|
||||
$user = $this->getUser();
|
||||
$user = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
||||
|
||||
if ($this->isGranted('ROLE_SUPER_ADMIN')) {
|
||||
$usersByOrganization = $this->userService->getUsersGroupedForIndex();
|
||||
$uo = $this->entityManager->getRepository(UsersOrganizations::class)->findUsersWithOrganization();
|
||||
$noOrgUsers = $this->userService->formatNoOrgUsersAsAssoc(
|
||||
$this->entityManager->getRepository(User::class)->findActiveUsersWithoutOrganization());
|
||||
$usersByOrganization = $this->userService->groupByOrganization($uo);
|
||||
$usersByOrganization += $noOrgUsers;
|
||||
|
||||
//Log action
|
||||
$this->actionService->createAction("View all users", $user, null, "All" );
|
||||
|
||||
|
||||
} elseif ($this->isGranted('ROLE_ADMIN')) {
|
||||
$usersByOrganization = $this->userService->getUsersGroupedForAdmin($user);
|
||||
dd("dsaf");
|
||||
} else {
|
||||
$usersByOrganization = [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use Doctrine\Persistence\ManagerRegistry;
|
|||
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
||||
use App\Entity\UsersOrganizations;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<User>
|
||||
|
|
@ -33,16 +34,6 @@ class UserRepository extends ServiceEntityRepository implements PasswordUpgrader
|
|||
$this->getEntityManager()->flush();
|
||||
}
|
||||
|
||||
// Rewrote this function to return less data
|
||||
public function getAllActiveUsers(): array{
|
||||
$queryBuilder = $this->createQueryBuilder('u')
|
||||
->select('u.surname', 'u.email', 'u.id', 'u.isActive', 'u.name', 'u.pictureUrl')
|
||||
->where('u.isActive = :isActive')
|
||||
->orderBy('u.surname', 'ASC');
|
||||
$queryBuilder->setParameter('isActive', true);
|
||||
return $queryBuilder->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns active users that are NOT in any UsersOrganizations mapping.
|
||||
* Returns User entities.
|
||||
|
|
@ -53,7 +44,7 @@ class UserRepository extends ServiceEntityRepository implements PasswordUpgrader
|
|||
{
|
||||
$qb = $this->createQueryBuilder('u')
|
||||
->select('u')
|
||||
->leftJoin('App\\Entity\\UsersOrganizations', 'uo', 'WITH', 'uo.users = u')
|
||||
->leftJoin(UsersOrganizations::class, 'uo', 'WITH', 'uo.users = u')
|
||||
->where('u.isActive = :uActive')
|
||||
->andWhere('u.isDeleted = :uDeleted')
|
||||
->andWhere('uo.id IS NULL')
|
||||
|
|
@ -63,32 +54,4 @@ class UserRepository extends ServiceEntityRepository implements PasswordUpgrader
|
|||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active users with their organization relationships
|
||||
* @return array
|
||||
*/
|
||||
public function getAllActiveUsersWithOrganizations(): array
|
||||
{
|
||||
$queryBuilder = $this->createQueryBuilder('u')
|
||||
->select('u', 'uo', 'o')
|
||||
->leftJoin('App\Entity\UsersOrganizations', 'uo', 'WITH', 'u.id = uo.users')
|
||||
->leftJoin('App\Entity\Organizations', 'o', 'WITH', 'uo.organization = o.id')
|
||||
->where('u.isActive = :isActive')
|
||||
->andWhere('u.isDeleted = :isDeleted')
|
||||
->andWhere('(uo.isActive = :uoActive OR uo.isActive IS NULL)')
|
||||
->andWhere('(o.isActive = :oActive OR o.isActive IS NULL)')
|
||||
->andWhere('(o.isDeleted = :oDeleted OR o.isDeleted IS NULL)')
|
||||
->orderBy('o.name', 'ASC')
|
||||
->addOrderBy('u.surname', 'ASC');
|
||||
|
||||
$queryBuilder->setParameter('isActive', true);
|
||||
$queryBuilder->setParameter('isDeleted', false);
|
||||
$queryBuilder->setParameter('uoActive', true);
|
||||
$queryBuilder->setParameter('oActive', true);
|
||||
$queryBuilder->setParameter('oDeleted', false);
|
||||
|
||||
return $queryBuilder->getQuery()->getResult();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,20 +22,18 @@ class UsersOrganizationsRepository extends ServiceEntityRepository
|
|||
*
|
||||
* @return UsersOrganizations[]
|
||||
*/
|
||||
public function findActiveWithUserAndOrganization(): array
|
||||
public function findUsersWithOrganization(): array
|
||||
{
|
||||
$qb = $this->createQueryBuilder('uo')
|
||||
->addSelect('u', 'o')
|
||||
->leftJoin('uo.users', 'u')
|
||||
->leftJoin('uo.organization', 'o')
|
||||
->where('uo.isActive = :uoActive')
|
||||
->andWhere('u.isActive = :uActive')
|
||||
->andWhere('u.isDeleted = :uDeleted')
|
||||
->andWhere('o.isActive = :oActive')
|
||||
->andWhere('o.isDeleted = :oDeleted')
|
||||
->orderBy('o.name', 'ASC')
|
||||
->addOrderBy('u.surname', 'ASC')
|
||||
->setParameter('uoActive', true)
|
||||
->setParameter('uActive', true)
|
||||
->setParameter('uDeleted', false)
|
||||
->setParameter('oActive', true)
|
||||
|
|
|
|||
|
|
@ -3,17 +3,26 @@
|
|||
namespace App\Service;
|
||||
|
||||
|
||||
use App\Entity\Roles;
|
||||
use App\Entity\User;
|
||||
use App\Entity\UserOrganizatonApp;
|
||||
use App\Entity\UsersOrganizations;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityNotFoundException;
|
||||
use Exception;
|
||||
use League\Bundle\OAuth2ServerBundle\Model\AccessToken;
|
||||
use Random\RandomException;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
|
||||
class UserService
|
||||
{
|
||||
|
||||
public function __construct(private readonly EntityManagerInterface $entityManager)
|
||||
public const NOT_FOUND = 'Entity not found';
|
||||
|
||||
public function __construct(private readonly EntityManagerInterface $entityManager,
|
||||
private readonly Security $security,
|
||||
)
|
||||
{
|
||||
// Constructor logic if needed
|
||||
}
|
||||
|
|
@ -45,7 +54,7 @@ class UserService
|
|||
*/
|
||||
public function isUserConnected(string $userIdentifier): bool
|
||||
{
|
||||
$now = new \DateTimeImmutable('now', new \DateTimeZone('Europe/Paris'));
|
||||
$now = new DateTimeImmutable();
|
||||
$tokens = $this->entityManager->getRepository(AccessToken::class)->findBy([
|
||||
'userIdentifier' => $userIdentifier,
|
||||
'revoked' => false
|
||||
|
|
@ -62,28 +71,103 @@ class UserService
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns UsersOrganizations rows joined with User and Organization, grouped by organization name.
|
||||
* @return array<string, array{ organization: ?object, users: array<int, array{ users: User, userOrganization: UsersOrganizations, is_connected: bool }> }>
|
||||
* Check if the user have the rights to access the page
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
private function groupUserOrganizationsByOrganizationFromRows(array $rows): array
|
||||
public function hasAccessTo(User $user): bool{
|
||||
if($user->getUserIdentifier() === $this->security->getUser()->getUserIdentifier()){
|
||||
return true;
|
||||
}
|
||||
$userOrganization = $this->entityManager->getRepository(UserOrganizatonApp::class)->findBy(['user' => $user]);
|
||||
if($userOrganization) {
|
||||
foreach ($userOrganization as $uo) {
|
||||
if ($this->isAdminOfOrganization($uo)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if($this->security->isGranted('ROLE_SUPER_ADMIN')){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is an admin of the organization
|
||||
* A user is considered an admin of an organization if they have the 'ROLE_ADMIN' AND have the link to the
|
||||
* entity role 'ROLE_ADMIN' in the UsersOrganizationsApp entity
|
||||
* (if he is admin for any application of the organization).
|
||||
*
|
||||
* @param UsersOrganizations $usersOrganizations
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function isAdminOfOrganization(UsersOrganizations $usersOrganizations): bool{
|
||||
$actingUser = $this->getUserByIdentifier($this->security->getUser()->getUserIdentifier());
|
||||
$uo = $this->entityManager->getRepository(UsersOrganizations::class)->findOneBy(['user' => $actingUser]);
|
||||
$roleAdmin = $this->entityManager->getRepository(Roles::class)->findOneBy(['role' => 'ADMIN']);
|
||||
if ($uo){
|
||||
$uoa = $this->entityManager->getRepository(UserOrganizatonApp::class)->findOneBy(['userOrganization' => $uo,
|
||||
'role'=> $roleAdmin]);
|
||||
if ($uoa && $this->security->isGranted('ROLE_ADMIN')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user by their identifier.
|
||||
*
|
||||
* @param string $userIdentifier
|
||||
* @return User|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getUserByIdentifier(string $userIdentifier): ?User
|
||||
{
|
||||
$user = $this->entityManager->getRepository(User::class)->findOneBy(['userIdentifier' => $userIdentifier]);
|
||||
if (!$user) {
|
||||
throw new EntityNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get users grouped by their organizations for the index page.
|
||||
* This method should return an array of users grouped by their organizations.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function groupByOrganization(array $usersOrganizations): array
|
||||
{
|
||||
$grouped = [];
|
||||
foreach ($rows as $userOrg) {
|
||||
$organization = $userOrg->getOrganization();
|
||||
$user = $userOrg->getUsers();
|
||||
$orgName = $organization?->getName() ?? 'No Organization';
|
||||
|
||||
if (!isset($grouped[$orgName])) {
|
||||
$grouped[$orgName] = [
|
||||
'organization' => $organization,
|
||||
foreach ($usersOrganizations as $userOrg) {
|
||||
$org = $userOrg->getOrganization();
|
||||
if (!$org) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$orgId = $org->getId();
|
||||
$orgName = $org->getName();
|
||||
|
||||
if (!isset($grouped[$orgId])) {
|
||||
$grouped[$orgId] = [
|
||||
'id' => $orgId,
|
||||
'name' => $orgName,
|
||||
'users' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$grouped[$orgName]['users'][] = [
|
||||
'users' => $user,
|
||||
'userOrganization' => $userOrg,
|
||||
'is_connected' => $this->isUserConnected($user->getEmail()),
|
||||
$user = $userOrg->getUsers();
|
||||
$grouped[$orgId]['users'][] = [
|
||||
'entity' => $user,
|
||||
'connected' => $this->isUserConnected($user->getUserIdentifier()),
|
||||
'isActive' => (bool) $userOrg->isActive()
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -91,64 +175,27 @@ class UserService
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns users that have no UsersOrganizations mapping.
|
||||
* @return array<int, array{ users: User, userOrganization: null, is_connected: bool }>
|
||||
* Format users without organization for admin view.
|
||||
*
|
||||
* @param array $users
|
||||
* @return array
|
||||
*/
|
||||
private function buildUsersWithoutOrganization(): array
|
||||
public function formatNoOrgUsersAsAssoc(array $noOrgUsers): array
|
||||
{
|
||||
$userRepository = $this->entityManager->getRepository(User::class);
|
||||
$users = $userRepository->findActiveUsersWithoutOrganization();
|
||||
$group = [
|
||||
'id' => null,
|
||||
'name' => 'Utilisateurs',
|
||||
'users' => [],
|
||||
];
|
||||
|
||||
$list = [];
|
||||
foreach ($users as $user) {
|
||||
$list[] = [
|
||||
'users' => $user,
|
||||
'userOrganization' => null,
|
||||
'is_connected' => $this->isUserConnected($user->getEmail()),
|
||||
foreach ($noOrgUsers as $user) {
|
||||
$group['users'][] = [
|
||||
'entity' => $user,
|
||||
'connected' => $this->isUserConnected($user->getUserIdentifier()),
|
||||
];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public API: build the final structure for the index view.
|
||||
* Groups users by organization and adds a "No Organization" group for users without mapping.
|
||||
* @return array<string, array{ organization: ?object, users: array<int, array{ users: User, userOrganization: UsersOrganizations|null, is_connected: bool }> }>
|
||||
*/
|
||||
public function getUsersGroupedForIndex(): array
|
||||
{
|
||||
$usersOrganizationsRepo = $this->entityManager->getRepository(UsersOrganizations::class);
|
||||
$rows = $usersOrganizationsRepo->findActiveWithUserAndOrganization();
|
||||
|
||||
$grouped = $this->groupUserOrganizationsByOrganizationFromRows($rows);
|
||||
$noOrgUsers = $this->buildUsersWithoutOrganization();
|
||||
|
||||
if (!empty($noOrgUsers)) {
|
||||
$grouped['No Organization'] = [
|
||||
'organization' => null,
|
||||
'users' => $noOrgUsers,
|
||||
];
|
||||
}
|
||||
|
||||
ksort($grouped);
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* For admins: return users grouped only for organizations where the given user is ADMIN.
|
||||
* @return array<string, array{ organization: ?object, users: array<int, array{ users: User, userOrganization: UsersOrganizations, is_connected: bool }> }>
|
||||
*/
|
||||
public function getUsersGroupedForAdmin(User $adminUser): array
|
||||
{
|
||||
$uoaRepo = $this->entityManager->getRepository(UserOrganizatonApp::class);
|
||||
$orgIds = $uoaRepo->findAdminOrganizationIdsForUser($adminUser);
|
||||
|
||||
$uoRepo = $this->entityManager->getRepository(UsersOrganizations::class);
|
||||
$rows = $uoRepo->findActiveWithUserAndOrganizationByOrganizationIds($orgIds);
|
||||
|
||||
$grouped = $this->groupUserOrganizationsByOrganizationFromRows($rows);
|
||||
ksort($grouped);
|
||||
return $grouped;
|
||||
// Use a fixed key (e.g., 0 or 'none') to avoid collisions with real org IDs
|
||||
return ['none' => $group];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>Gestion Utilisateurs</h1>
|
||||
{% if is_granted('ROLE_SUPER_ADMIN') %}
|
||||
<a href="{{ path('user_new') }}" class="btn btn-primary">Ajouter un utilisateur</a>
|
||||
{# <a href="{{ path('user_new') }}" class="btn btn-primary">Ajouter un utilisateur</a>#}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
|
@ -15,14 +15,13 @@
|
|||
{% if usersByOrganization|length == 0 %}
|
||||
<div class="alert alert-info">
|
||||
<h4>Aucun utilisateur trouvé</h4>
|
||||
<p>Il n'y a actuellement aucun utilisateur correspondant à votre périmètre.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{% for orgName, orgData in usersByOrganization %}
|
||||
{% for org in usersByOrganization %}
|
||||
{% include 'user/userList.html.twig' with {
|
||||
title: orgName,
|
||||
org: orgData,
|
||||
organizationId: orgData.organization ? orgData.organization.id : null
|
||||
title: org.name,
|
||||
organizationId: org.id|default(null),
|
||||
users: org.users
|
||||
} %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<div class="card-title d-flex justify-content-between align-items-center ">
|
||||
<h3>{{ title }}</h3>
|
||||
{% if organizationId %}
|
||||
{# <span class="badge bg-primary">ID: {{ organizationId }}</span>#}
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
@ -24,47 +24,62 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if org.users|length == 0 %}
|
||||
{% if users|length == 0 %}
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">Aucun utilisateur trouvé dans cette organisation.</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
{% for userData in org.users %}
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if userData.users.pictureUrl %}
|
||||
<img src="{{ asset(userData.users.pictureUrl) }}" alt="User profile pic"
|
||||
{% if user.entity.pictureUrl %}
|
||||
<img src="{{ asset(user.entity.pictureUrl) }}" alt="User profile pic"
|
||||
class="rounded-circle"
|
||||
style="width:40px; height:40px;">
|
||||
{% else %}
|
||||
<div class="rounded-circle bg-secondary d-flex align-items-center justify-content-center"
|
||||
style="width:40px; height:40px;">
|
||||
<span class="text-white">{{ userData.users.name|first|upper }}{{ userData.users.surname|first|upper }}</span>
|
||||
<span class="text-white">{{ user.entity.name|first|upper }}{{ user.entity.surname|first|upper }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ userData.users.surname }}</td>
|
||||
<td>{{ userData.users.name }}</td>
|
||||
<td>{{ userData.users.email }}</td>
|
||||
<td>{{ user.entity.surname }}</td>
|
||||
<td>{{ user.entity.name }}</td>
|
||||
<td>{{ user.entity.email }}</td>
|
||||
{# Logic for status #}
|
||||
<td>
|
||||
{% if userData.is_connected %}
|
||||
<span class="badge bg-success">Actif</span>
|
||||
{# check if the user is active in the organization link #}
|
||||
{% if user.isActive is defined %}
|
||||
{% if user.isActive %}
|
||||
{% if user.connected %}
|
||||
<span class="badge bg-success">Actif</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Inactif</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="badge bg-danger">Désactivé</span>
|
||||
{% endif %}
|
||||
{# if no organization link #}
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Inactif</span>
|
||||
{% if user.connected %}
|
||||
<span class="badge bg-success">Actif</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Inactif</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{# {% if organizationId is defined and organizationId %}#}
|
||||
{# <a href="{{ path('user_show', {'id': userData.users.id, 'organizationId': organizationId}) }}"#}
|
||||
{# class="p-3 align-middle color-primary">#}
|
||||
{# {{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }}#}
|
||||
{# </a>#}
|
||||
{# {% else %}#}
|
||||
{# <a href="{{ path('user_show', {'id': userData.users.id}) }}"#}
|
||||
{# class="p-3 align-middle color-primary">#}
|
||||
{# {{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }}#}
|
||||
{# </a>#}
|
||||
{# {% endif %}#}
|
||||
{# {% if organizationId is defined and organizationId %} #}
|
||||
{# <a href="{{ path('user_show', {'id': user.entity.id, 'organizationId': organizationId}) }}" #}
|
||||
{# class="p-3 align-middle color-primary"> #}
|
||||
{# {{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }} #}
|
||||
{# </a> #}
|
||||
{# {% else %} #}
|
||||
{# <a href="{{ path('user_show', {'id': user.entity.id}) }}" #}
|
||||
{# class="p-3 align-middle color-primary"> #}
|
||||
{# {{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }} #}
|
||||
{# </a> #}
|
||||
{# {% endif %} #}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
|||
Loading…
Reference in New Issue