enabled ajax function to edit user
This commit is contained in:
parent
c2ea41f0a1
commit
b9b0efd6c6
|
|
@ -27,7 +27,7 @@ export default class extends base_controller {
|
||||||
orgId: Number
|
orgId: Number
|
||||||
}
|
}
|
||||||
|
|
||||||
static targets = ["select", "statusButton", "modal", "userSelect", "appList"];
|
static targets = ["select", "statusButton", "modal", "userSelect", "appList", "emailInput", "phoneInput", "nameInput", "surnameInput"];
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
this.roleSelect();
|
this.roleSelect();
|
||||||
|
|
@ -1055,4 +1055,69 @@ export default class extends base_controller {
|
||||||
alert("Erreur réseau.");
|
alert("Erreur réseau.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async openEditUserModal(event) {
|
||||||
|
const userId = event.currentTarget.dataset.id;
|
||||||
|
this.currentUserId = userId;
|
||||||
|
this.modal.show();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Fetch all available apps using your shared base method
|
||||||
|
await this.fetchAndRenderApplications(this.appListTarget);
|
||||||
|
|
||||||
|
// 2. Fetch specific user data WITH the orgId query parameter
|
||||||
|
// We use this.orgIdValue which is mapped to data-user-org-id-value
|
||||||
|
const response = await fetch(`/user/data/${userId}?orgId=${this.orgIdValue}`);
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch user data');
|
||||||
|
|
||||||
|
const user = await response.json();
|
||||||
|
|
||||||
|
// 3. Fill text inputs
|
||||||
|
this.emailInputTarget.value = user.email;
|
||||||
|
this.phoneInputTarget.value = user.phoneNumber || '';
|
||||||
|
this.nameInputTarget.value = user.name;
|
||||||
|
this.surnameInputTarget.value = user.surname;
|
||||||
|
|
||||||
|
// 4. Check the application boxes
|
||||||
|
const checkboxes = this.appListTarget.querySelectorAll('input[type="checkbox"]');
|
||||||
|
|
||||||
|
// Ensure we handle IDs as strings or numbers consistently
|
||||||
|
const activeAppIds = user.applicationIds.map(id => id.toString());
|
||||||
|
|
||||||
|
checkboxes.forEach(cb => {
|
||||||
|
cb.checked = activeAppIds.includes(cb.value.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur lors du chargement des données utilisateur:", error);
|
||||||
|
alert("Impossible de charger les informations de l'utilisateur.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitEditUser(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const formData = new FormData(event.target);
|
||||||
|
|
||||||
|
// Force Uppercase on Surname as requested
|
||||||
|
formData.set('surname', formData.get('surname').toUpperCase());
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/user/edit/${this.currentUserId}/ajax`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
this.modal.hide();
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
const result = await response.json();
|
||||||
|
alert(result.error || "Erreur lors de la mise à jour.");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert("Erreur réseau.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -913,5 +913,93 @@ class UserController extends AbstractController
|
||||||
return $this->json(['error' => 'Une erreur interne est survenue.'], 500);
|
return $this->json(['error' => 'Une erreur interne est survenue.'], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/data/{id}', name: 'user_data_json', methods: ['GET'])]
|
||||||
|
public function userData(User $user, Request $request): JsonResponse {
|
||||||
|
$orgId = $request->query->get('orgId');
|
||||||
|
$org = $this->organizationRepository->find($orgId);
|
||||||
|
if (!$org) {
|
||||||
|
$this->loggerService->logEntityNotFound('Organization', ['id' => $orgId], $this->getUser()->getUserIdentifier());
|
||||||
|
return $this->json(['error' => "L'organisation n'existe pas."], 404);
|
||||||
|
}
|
||||||
|
$uo = $this->uoRepository->findOneBy(['users' => $user, 'organization' => $org]);
|
||||||
|
$apps = $this->userOrganizationAppService->getUserApplicationByOrganization($uo);
|
||||||
|
return $this->json([
|
||||||
|
'email' => $user->getEmail(),
|
||||||
|
'name' => $user->getName(),
|
||||||
|
'surname' => $user->getSurname(),
|
||||||
|
'phoneNumber' => $user->getPhoneNumber(),
|
||||||
|
'applicationIds' => array_map(fn($app) => $app->getId(), $apps),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/edit/{id}/ajax', name: 'edit_ajax', methods: ['POST'])]
|
||||||
|
public function editAjax(int $id, Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$this->denyAccessUnlessGranted('ROLE_USER');
|
||||||
|
$actingUser = $this->getUser();
|
||||||
|
|
||||||
|
$user = $this->userRepository->find($id);
|
||||||
|
if (!$user) {
|
||||||
|
return $this->json(['error' => "L'utilisateur n'existe pas."], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!$this->userService->isAdminOfUser($user)) {
|
||||||
|
return $this->json(['error' => "Accès non autorisé."], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $request->request->all();
|
||||||
|
$orgId = $data['organizationId'] ?? null;
|
||||||
|
$selectedApps = $data['applications'] ?? [];
|
||||||
|
|
||||||
|
// 1. Clean data for the form (remove non-entity fields)
|
||||||
|
unset($data['organizationId'], $data['applications']);
|
||||||
|
|
||||||
|
$form = $this->createForm(UserForm::class, $user, [
|
||||||
|
'csrf_protection' => false,
|
||||||
|
'allow_extra_fields' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$form->submit($data, false);
|
||||||
|
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
// 2. Handle User Info & Picture
|
||||||
|
$picture = $request->files->get('pictureUrl');
|
||||||
|
$this->userService->formatUserData($user, $picture);
|
||||||
|
$user->setModifiedAt(new \DateTimeImmutable('now'));
|
||||||
|
|
||||||
|
$this->entityManager->persist($user);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
// 3. Handle Organization-specific Application Sync
|
||||||
|
if ($orgId) {
|
||||||
|
$org = $this->organizationRepository->find($orgId);
|
||||||
|
if ($org) {
|
||||||
|
// Logic to sync applications for THIS specific organization
|
||||||
|
$uo = $this->uoRepository->findOneBy(['users' => $user, 'organization' => $org]);
|
||||||
|
$this->userOrganizationAppService->syncUserApplicationsByOrganization($uo, $selectedApps);
|
||||||
|
|
||||||
|
// Create Action Log
|
||||||
|
$this->actionService->createAction("Edit user information", $actingUser, $org, $user->getUserIdentifier());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Logging
|
||||||
|
$this->loggerService->logUserAction($user->getId(), $actingUser->getUserIdentifier(), 'User information edited via AJAX');
|
||||||
|
if ($this->isGranted('ROLE_SUPER_ADMIN')) {
|
||||||
|
$this->loggerService->logSuperAdmin($user->getId(), $actingUser->getUserIdentifier(), "Super Admin edited user via AJAX");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->json(['success' => true, 'message' => 'Informations modifiées avec succès.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->json(['error' => 'Données de formulaire invalides.'], 400);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->errorLogger->critical($e->getMessage());
|
||||||
|
return $this->json(['error' => 'Une erreur est survenue lors de la modification.'], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use App\Repository\UserOrganizatonAppRepository;
|
use App\Repository\UserOrganizationAppRepository;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
#[ORM\Entity(repositoryClass: UserOrganizatonAppRepository::class)]
|
#[ORM\Entity(repositoryClass: UserOrganizationAppRepository::class)]
|
||||||
class UserOrganizationApp
|
class UserOrganizationApp
|
||||||
{
|
{
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use Doctrine\Persistence\ManagerRegistry;
|
||||||
/**
|
/**
|
||||||
* @extends ServiceEntityRepository<UserOrganizationApp>
|
* @extends ServiceEntityRepository<UserOrganizationApp>
|
||||||
*/
|
*/
|
||||||
class UserOrganizatonAppRepository extends ServiceEntityRepository
|
class UserOrganizationAppRepository extends ServiceEntityRepository
|
||||||
{
|
{
|
||||||
public function __construct(ManagerRegistry $registry)
|
public function __construct(ManagerRegistry $registry)
|
||||||
{
|
{
|
||||||
|
|
@ -7,6 +7,8 @@ use App\Entity\Roles;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Entity\UserOrganizationApp;
|
use App\Entity\UserOrganizationApp;
|
||||||
use App\Entity\UsersOrganizations;
|
use App\Entity\UsersOrganizations;
|
||||||
|
use App\Repository\UserOrganizationAppRepository;
|
||||||
|
use App\Repository\UsersOrganizationsRepository;
|
||||||
use App\Service\ActionService;
|
use App\Service\ActionService;
|
||||||
use App\Service\LoggerService;
|
use App\Service\LoggerService;
|
||||||
use App\Service\UserService;
|
use App\Service\UserService;
|
||||||
|
|
@ -16,7 +18,15 @@ use Symfony\Bundle\SecurityBundle\Security;
|
||||||
|
|
||||||
class UserOrganizationAppService
|
class UserOrganizationAppService
|
||||||
{
|
{
|
||||||
public function __construct(private readonly EntityManagerInterface $entityManager, private readonly ActionService $actionService, private readonly Security $security, private readonly UserService $userService, private readonly LoggerInterface $logger, private readonly LoggerService $loggerService)
|
|
||||||
|
public function __construct(private readonly EntityManagerInterface $entityManager,
|
||||||
|
private readonly ActionService $actionService,
|
||||||
|
private readonly Security $security,
|
||||||
|
private readonly UserService $userService,
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
|
private readonly LoggerService $loggerService,
|
||||||
|
private readonly UsersOrganizationsRepository $usersOrganizationsRepository,
|
||||||
|
private readonly UserOrganizationAppRepository $uoaRepository)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,9 +86,9 @@ class UserOrganizationAppService
|
||||||
public function deactivateAllUserOrganizationsAppLinks(UsersOrganizations $userOrganization, Apps $app = null): void
|
public function deactivateAllUserOrganizationsAppLinks(UsersOrganizations $userOrganization, Apps $app = null): void
|
||||||
{
|
{
|
||||||
if($app) {
|
if($app) {
|
||||||
$uoas = $this->entityManager->getRepository(UserOrganizationApp::class)->findBy(['userOrganization' => $userOrganization, 'application' => $app, 'isActive' => true]);
|
$uoas = $this->uoaRepository->findBy(['userOrganization' => $userOrganization, 'application' => $app, 'isActive' => true]);
|
||||||
} else {
|
} else {
|
||||||
$uoas = $this->entityManager->getRepository(UserOrganizationApp::class)->findBy(['userOrganization' => $userOrganization, 'isActive' => true]);
|
$uoas = $this->uoaRepository->findBy(['userOrganization' => $userOrganization, 'isActive' => true]);
|
||||||
}
|
}
|
||||||
foreach ($uoas as $uoa) {
|
foreach ($uoas as $uoa) {
|
||||||
try{
|
try{
|
||||||
|
|
@ -98,156 +108,6 @@ class UserOrganizationAppService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Synchronizes user roles for a specific application within an organization.
|
|
||||||
*
|
|
||||||
* This method handles the complete lifecycle of user-application role assignments:
|
|
||||||
* - Activates/deactivates existing role links based on selection
|
|
||||||
* - Creates new role assignments for newly selected roles
|
|
||||||
* - Updates the user's global Symfony security roles when ADMIN/SUPER_ADMIN roles are assigned
|
|
||||||
*
|
|
||||||
* @param UsersOrganizations $uo The user-organization relationship
|
|
||||||
* @param Apps $application The target application
|
|
||||||
* @param array $selectedRoleIds Array of role IDs that should be active for this user-app combination
|
|
||||||
* @param User $actingUser The user performing this action (for audit logging)
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @throws \Exception If role entities cannot be found or persisted
|
|
||||||
*/
|
|
||||||
public function syncRolesForUserOrganizationApp(
|
|
||||||
UsersOrganizations $uo,
|
|
||||||
Apps $application,
|
|
||||||
array $selectedRoleIds,
|
|
||||||
User $actingUser
|
|
||||||
): void {
|
|
||||||
|
|
||||||
// Fetch existing UserOrganizationApp links for this user and application
|
|
||||||
$uoas = $this->entityManager->getRepository(UserOrganizationApp::class)->findBy([
|
|
||||||
'userOrganization' => $uo,
|
|
||||||
'application' => $application,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$currentRoleIds = [];
|
|
||||||
// Process existing role links - activate or deactivate based on selection
|
|
||||||
foreach ($uoas as $uoa) {
|
|
||||||
$roleId = $uoa->getRole()->getId();
|
|
||||||
$currentRoleIds[] = $roleId;
|
|
||||||
$roleName = $uoa->getRole()->getName();
|
|
||||||
|
|
||||||
if (in_array((string) $roleId, $selectedRoleIds, true)) {
|
|
||||||
// Role is selected - ensure it's active
|
|
||||||
if (!$uoa->isActive()) {
|
|
||||||
$uoa->setIsActive(true);
|
|
||||||
$this->entityManager->persist($uoa);
|
|
||||||
$this->loggerService->logOrganizationInformation(
|
|
||||||
$uo->getOrganization()->getId(),
|
|
||||||
$actingUser->getId(),
|
|
||||||
"Re-activated role '$roleName' for user '{$uo->getUsers()->getId()}' in application '{$application->getName()} with UOA ID {$uoa->getId()}'"
|
|
||||||
);
|
|
||||||
$this->actionService->createAction(
|
|
||||||
"Re-activate user role for application",
|
|
||||||
$actingUser,
|
|
||||||
$uo->getOrganization(),
|
|
||||||
"App: {$application->getName()}, Role: $roleName for user {$uo->getUsers()->getUserIdentifier()}"
|
|
||||||
);
|
|
||||||
// Sync Admins roles to user's global Symfony security roles
|
|
||||||
if (in_array($roleName, ['ADMIN', 'SUPER ADMIN'], true)) {
|
|
||||||
$this->userService->syncUserRoles($uo->getUsers(), $roleName, true);
|
|
||||||
}
|
|
||||||
// Ensure ADMIN role is assigned if SUPER ADMIN is activated
|
|
||||||
if ($roleName === 'SUPER ADMIN') {
|
|
||||||
$this->ensureAdminRoleForSuperAdmin($uoa);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Role is not selected - ensure it's inactive
|
|
||||||
if ($uoa->isActive()) {
|
|
||||||
$uoa->setIsActive(false);
|
|
||||||
$this->entityManager->persist($uoa);
|
|
||||||
$this->loggerService->logOrganizationInformation(
|
|
||||||
$uo->getOrganization()->getId(),
|
|
||||||
$actingUser->getId(),
|
|
||||||
"Deactivated role '$roleName' for user '{$uo->getUsers()->getId()}' in application '{$application->getName()}' with UOA ID {$uoa->getId()}'"
|
|
||||||
);
|
|
||||||
$this->actionService->createAction(
|
|
||||||
"Deactivate user role for application",
|
|
||||||
$actingUser,
|
|
||||||
$uo->getOrganization(),
|
|
||||||
"App: {$application->getName()}, Role: $roleName for user {$uo->getUsers()->getUserIdentifier()}"
|
|
||||||
);
|
|
||||||
// Sync Admins roles to user's global Symfony security roles
|
|
||||||
if (in_array($roleName, ['ADMIN', 'SUPER ADMIN'], true)) {
|
|
||||||
$this->userService->syncUserRoles($uo->getUsers(), $roleName, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new role assignments for roles that don't exist yet
|
|
||||||
foreach ($selectedRoleIds as $roleId) {
|
|
||||||
if (!in_array($roleId, $currentRoleIds)) {
|
|
||||||
$role = $this->entityManager->getRepository(Roles::class)->find($roleId);
|
|
||||||
if ($role) {
|
|
||||||
// Create new user-organization-application role link
|
|
||||||
$newUoa = new UserOrganizationApp();
|
|
||||||
$newUoa->setUserOrganization($uo);
|
|
||||||
$newUoa->setApplication($application);
|
|
||||||
$newUoa->setRole($role);
|
|
||||||
$newUoa->setIsActive(true);
|
|
||||||
|
|
||||||
// Sync Admins roles to user's global Symfony security roles
|
|
||||||
if (in_array($role->getName(), ['ADMIN', 'SUPER ADMIN'], true)) {
|
|
||||||
$this->userService->syncUserRoles($uo->getUsers(), $role->getName(), true);
|
|
||||||
}
|
|
||||||
// Ensure ADMIN role is assigned if SUPER ADMIN is activated
|
|
||||||
if ($role->getName() === 'SUPER ADMIN') {
|
|
||||||
$this->ensureAdminRoleForSuperAdmin($newUoa);
|
|
||||||
}
|
|
||||||
$this->entityManager->persist($newUoa);
|
|
||||||
$this->loggerService->logOrganizationInformation(
|
|
||||||
$uo->getOrganization()->getId(),
|
|
||||||
$actingUser->getId(),
|
|
||||||
"Created new role '{$role->getName()}' for user '{$uo->getUsers()->getId()}' in application '{$application->getName()}' with UOA ID {$newUoa->getId()}'"
|
|
||||||
);
|
|
||||||
$this->actionService->createAction("New user role for application",
|
|
||||||
$actingUser,
|
|
||||||
$uo->getOrganization(),
|
|
||||||
"App: {$application->getName()}, Role: {$role->getName()} for user {$uo->getUsers()->getUserIdentifier()}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->entityManager->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attribute the role Admin to the user if the user has the role Super Admin
|
|
||||||
*
|
|
||||||
* @param UserOrganizationApp $uoa
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function ensureAdminRoleForSuperAdmin(UserOrganizationApp $uoa): void
|
|
||||||
{
|
|
||||||
$uoaAdmin = $this->entityManager->getRepository(UserOrganizationApp::class)->findOneBy([
|
|
||||||
'userOrganization' => $uoa->getUserOrganization(),
|
|
||||||
'application' => $uoa->getApplication(),
|
|
||||||
'role' => $this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'ADMIN'])
|
|
||||||
]);
|
|
||||||
if(!$uoaAdmin) {
|
|
||||||
$uoaAdmin = new UserOrganizationApp();
|
|
||||||
$uoaAdmin->setUserOrganization($uoa->getUserOrganization());
|
|
||||||
$uoaAdmin->setApplication($uoa->getApplication());
|
|
||||||
$uoaAdmin->setRole($this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'ADMIN']));
|
|
||||||
$uoaAdmin->setIsActive(true);
|
|
||||||
$this->entityManager->persist($uoaAdmin);
|
|
||||||
}
|
|
||||||
// If the ADMIN role link exists but is inactive, activate it
|
|
||||||
if ($uoaAdmin && !$uoaAdmin->isActive()) {
|
|
||||||
$uoaAdmin->setIsActive(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get users applications links for a given user
|
* Get users applications links for a given user
|
||||||
|
|
@ -257,10 +117,10 @@ class UserOrganizationAppService
|
||||||
*/
|
*/
|
||||||
public function getUserApplications(User $user): array
|
public function getUserApplications(User $user): array
|
||||||
{
|
{
|
||||||
$uos = $this->entityManager->getRepository(UsersOrganizations::class)->findBy(['users' => $user]);
|
$uos = $this->entityManager->getRepository(UsersOrganizations::class)->findBy(['users' => $user, 'isActive' => true]);
|
||||||
$apps = [];
|
$apps = [];
|
||||||
foreach ($uos as $uo) {
|
foreach ($uos as $uo) {
|
||||||
$uoas = $this->entityManager->getRepository(UserOrganizationApp::class)->findBy(['userOrganization' => $uo, 'isActive' => true]);
|
$uoas = $this->uoaRepository->findBy(['userOrganization' => $uo, 'isActive' => true]);
|
||||||
foreach ($uoas as $uoa) {
|
foreach ($uoas as $uoa) {
|
||||||
$app = $uoa->getApplication();
|
$app = $uoa->getApplication();
|
||||||
if (!in_array($app, $apps, true)) {
|
if (!in_array($app, $apps, true)) {
|
||||||
|
|
@ -270,4 +130,82 @@ class UserOrganizationAppService
|
||||||
}
|
}
|
||||||
return $apps;
|
return $apps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUserApplicationByOrganization(UsersOrganizations $uo): array
|
||||||
|
{
|
||||||
|
$uoas = $this->uoaRepository->findBy(['userOrganization' => $uo, 'isActive' => true]);
|
||||||
|
$apps = [];
|
||||||
|
foreach ($uoas as $uoa) {
|
||||||
|
$app = $uoa->getApplication();
|
||||||
|
if (!in_array($app, $apps, true)) {
|
||||||
|
$apps[] = $app;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $apps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function syncUserApplicationsByOrganization(UsersOrganizations $uo, array $selectedApps): void
|
||||||
|
{
|
||||||
|
// 1. Get all currently active applications for this specific User-Organization link
|
||||||
|
$currentUolas = $this->uoaRepository->findBy([
|
||||||
|
'userOrganization' => $uo,
|
||||||
|
'isActive' => true
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Track which app IDs are currently active in the DB
|
||||||
|
$currentAppIds = array_map(fn($uoa) => $uoa->getApplication()->getId(), $currentUolas);
|
||||||
|
|
||||||
|
// 2. REMOVAL: Deactivate apps that are in the DB but NOT in the new selection
|
||||||
|
foreach ($currentUolas as $uoa) {
|
||||||
|
$appId = $uoa->getApplication()->getId();
|
||||||
|
if (!in_array($appId, $selectedApps)) {
|
||||||
|
$uoa->setIsActive(false);
|
||||||
|
$this->actionService->createAction(
|
||||||
|
"Deactivate UOA link",
|
||||||
|
$uo->getUsers(),
|
||||||
|
$uo->getOrganization(),
|
||||||
|
"App: " . $uoa->getApplication()->getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. ADDITION / REACTIVATION: Handle the selected apps
|
||||||
|
foreach ($selectedApps as $appId) {
|
||||||
|
$app = $this->entityManager->getRepository(Apps::class)->find($appId);
|
||||||
|
if (!$app) continue;
|
||||||
|
|
||||||
|
// Check if a record (active or inactive) already exists
|
||||||
|
$existingUOA = $this->uoaRepository->findOneBy([
|
||||||
|
'userOrganization' => $uo,
|
||||||
|
'application' => $app
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$existingUOA) {
|
||||||
|
// Create new if it never existed
|
||||||
|
$newUOA = new UserOrganizationApp();
|
||||||
|
$newUOA->setUserOrganization($uo);
|
||||||
|
$newUOA->setApplication($app);
|
||||||
|
$newUOA->setIsActive(true);
|
||||||
|
$this->entityManager->persist($newUOA);
|
||||||
|
|
||||||
|
$this->actionService->createAction(
|
||||||
|
"Activate UOA link",
|
||||||
|
$uo->getUsers(),
|
||||||
|
$uo->getOrganization(),
|
||||||
|
"App: " . $app->getName()
|
||||||
|
);
|
||||||
|
} elseif (!$existingUOA->isActive()) {
|
||||||
|
// Reactivate if it was previously disabled
|
||||||
|
$existingUOA->setIsActive(true);
|
||||||
|
$this->actionService->createAction(
|
||||||
|
"Reactivate UOA link",
|
||||||
|
$uo->getUsers(),
|
||||||
|
$uo->getOrganization(),
|
||||||
|
"App: " . $app->getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->entityManager->flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,111 +33,18 @@
|
||||||
{% include 'user/userInformation.html.twig' %}
|
{% include 'user/userInformation.html.twig' %}
|
||||||
|
|
||||||
|
|
||||||
<div class="card border-0 no-header-bg ">
|
{# <div class="card border-0 no-header-bg ">#}
|
||||||
<div class="card-header">
|
{# <div class="card-header">#}
|
||||||
<div class="card-title">
|
{# <div class="card-title">#}
|
||||||
<h1>Information d'organisation</h1>
|
{# <h1>Information d'organisation</h1>#}
|
||||||
</div>
|
{# </div>#}
|
||||||
</div>
|
{# </div>#}
|
||||||
<div class="card-body ms-4">
|
{# <div class="card-body ms-4">#}
|
||||||
{# TODO: dynamic number of project#}
|
{# TODO: dynamic number of project#}
|
||||||
<p><b>Projet : </b>69 projets vous sont attribués</p>
|
{# <p><b>Projet : </b>69 projets vous sont attribués</p>#}
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
{# <div class="card-body">#}
|
|
||||||
{# <div class="row g-2">#}
|
|
||||||
{# {% for app in apps %}#}
|
|
||||||
{# <div class="col-12 col-md-6">#}
|
|
||||||
{# <div class="card h-100">#}
|
|
||||||
{# <div class="card-header d-flex gap-2">#}
|
|
||||||
{# {% if app.logoMiniUrl %}#}
|
|
||||||
{# <img src="{{ asset(appli.entity.logoMiniUrl) }}" alt="Logo {{ app.name }}"#}
|
|
||||||
{# class="rounded-circle" style="width:50px; height:50px;">#}
|
|
||||||
{# {% endif %}#}
|
|
||||||
{# <div class="card-title">#}
|
|
||||||
{# <h1>{{ app.name|title }}</h1>#}
|
|
||||||
{# </div>#}
|
|
||||||
{# </div>#}
|
|
||||||
|
|
||||||
{# <div class="card-body">#}
|
|
||||||
{# <div class="row">#}
|
|
||||||
{# <p>#}
|
|
||||||
{# <b>Description :</b>#}
|
|
||||||
{# {{ app.descriptionSmall|default('Aucune description disponible.')|raw }}#}
|
|
||||||
{# </p>#}
|
|
||||||
{# </div>#}
|
|
||||||
|
|
||||||
{# #}{# find appGroup once, used in both editable and read-only branches #}
|
|
||||||
{# {% set appGroup = data.uoas[app.id]|default(null) %}#}
|
|
||||||
|
|
||||||
{# {% if canEdit %}#}
|
|
||||||
{# <form method="POST"#}
|
|
||||||
{# action="{{ path('user_application_role', { id: data.singleUo.id }) }}">#}
|
|
||||||
{# <div class="form-group mb-3">#}
|
|
||||||
{# <label for="roles-{{ app.id }}"><b>Rôles :</b></label>#}
|
|
||||||
{# <div class="form-check">#}
|
|
||||||
{# {% if appGroup %}#}
|
|
||||||
{# {% for role in data.rolesArray %}#}
|
|
||||||
{# <input class="form-check-input" type="checkbox"#}
|
|
||||||
{# name="roles[]"#}
|
|
||||||
{# value="{{ role.id }}"#}
|
|
||||||
{# id="role-{{ role.id }}-app-{{ app.id }}"#}
|
|
||||||
{# {% if role.id in appGroup.selectedRoleIds %}checked{% endif %}>#}
|
|
||||||
{# <label class="form-check"#}
|
|
||||||
{# for="role-{{ role.id }}-app-{{ app.id }}">#}
|
|
||||||
{# {% if role.name == 'USER' %}#}
|
|
||||||
{# Accès#}
|
|
||||||
{# {% else %}#}
|
|
||||||
{# {{ role.name|capitalize }}#}
|
|
||||||
{# {% endif %}#}
|
|
||||||
{# </label>#}
|
|
||||||
{# {% endfor %}#}
|
|
||||||
{# {% else %}#}
|
|
||||||
{# <p class="text-muted">Aucun rôle défini pour cette application.</p>#}
|
|
||||||
{# {% endif %}#}
|
|
||||||
{# </div>#}
|
|
||||||
{# <button type="submit" name="appId" value="{{ app.id }}"#}
|
|
||||||
{# class="btn btn-primary mt-2">#}
|
|
||||||
{# Sauvegarder#}
|
|
||||||
{# </button>#}
|
|
||||||
{# </div>#}
|
|
||||||
{# </form>#}
|
|
||||||
{# {% else %}#}
|
|
||||||
{# <div class="form-group mb-3">#}
|
|
||||||
{# <label for="roles-{{ app.id }}"><b>Rôles :</b></label>#}
|
|
||||||
{# <div class="form-check">#}
|
|
||||||
{# {% if appGroup %}#}
|
|
||||||
{# {% for role in data.rolesArray %}#}
|
|
||||||
{# <input class="form-check-input" type="checkbox"#}
|
|
||||||
{# disabled#}
|
|
||||||
{# name="roles[]"#}
|
|
||||||
{# value="{{ role.id }}"#}
|
|
||||||
{# id="role-{{ role.id }}-app-{{ app.id }}"#}
|
|
||||||
{# {% if role.id in appGroup.selectedRoleIds %}checked{% endif %}>#}
|
|
||||||
{# <label class="form-check"#}
|
|
||||||
{# for="role-{{ role.id }}-app-{{ app.id }}">#}
|
|
||||||
{# {% if role.name == 'USER' %}#}
|
|
||||||
{# Accès#}
|
|
||||||
{# {% else %}#}
|
|
||||||
{# {{ role.name|capitalize }}#}
|
|
||||||
{# {% endif %}#}
|
|
||||||
{# </label>#}
|
|
||||||
{# {% endfor %}#}
|
|
||||||
{# {% else %}#}
|
|
||||||
{# <p class="text-muted">Aucun rôle défini pour cette application.</p>#}
|
|
||||||
{# {% endif %}#}
|
|
||||||
{# </div>#}
|
|
||||||
{# </div>#}
|
|
||||||
{# {% endif %}#}
|
|
||||||
{# </div>#}
|
|
||||||
{# </div>#}
|
|
||||||
{# </div>#}
|
|
||||||
{# {% endfor %}#}
|
|
||||||
{# </div>#}
|
|
||||||
{# </div>#}
|
{# </div>#}
|
||||||
|
|
||||||
</div>
|
{# </div>#}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,80 @@
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="card no-header-bg border-0 ">
|
<div class="card no-header-bg border-0"
|
||||||
|
data-controller="user"
|
||||||
|
data-user-org-id-value="{{organizationId}}">
|
||||||
|
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
{% if user.pictureUrl is not empty %}
|
{% if user.pictureUrl is not empty %}
|
||||||
<img src="{{asset(user.pictureUrl)}}" alt="user" class="rounded-circle"
|
<img src="{{ asset(user.pictureUrl) }}" alt="user" class="rounded-circle" style="width:40px; height:40px;">
|
||||||
style="width:40px; height:40px;">
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="card-title ">
|
<div class="card-title">
|
||||||
<h2>{{ user.surname|capitalize }} {{ user.name|capitalize }}</h2>
|
<h2>{{ user.surname|capitalize }} {{ user.name|capitalize }}</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<a href="{{ path('user_edit', {'id': user.id}) }}"
|
{# Trigger the edit modal with the user ID #}
|
||||||
class="btn btn-primary">Modifier</a>
|
<button type="button" class="btn btn-primary"
|
||||||
|
data-action="click->user#openEditUserModal"
|
||||||
|
data-id="{{ user.id }}">
|
||||||
|
Modifier
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="card-body ms-4">
|
<div class="card-body ms-4">
|
||||||
<p><b>Email: </b>{{ user.email }}</p>
|
<p><b>Email: </b>{{ user.email }}</p>
|
||||||
<p><b>Dernière connection: </b>{{ user.lastConnection|date('d/m/Y') }}
|
<p><b>Dernière connection: </b>{{ user.lastConnection|date('d/m/Y') }} à {{ user.lastConnection|date('H:m') }}</p>
|
||||||
à {{ user.lastConnection|date('H:m:s') }} </p>
|
|
||||||
<p><b>Compte crée le: </b>{{ user.createdAt|date('d/m/Y') }}</p>
|
<p><b>Compte crée le: </b>{{ user.createdAt|date('d/m/Y') }}</p>
|
||||||
<p><b>Numéro de téléphone: </b>{{ user.phoneNumber ? user.phoneNumber : 'Non renseigné' }}</p>
|
<p><b>Numéro de téléphone: </b>{{ user.phoneNumber ? user.phoneNumber : 'Non renseigné' }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{# Reusable Edit Modal #}
|
||||||
|
<div class="modal fade" id="editUserModal" tabindex="-1" aria-hidden="true" data-user-target="modal">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Modifier l'utilisateur</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<form data-action="submit->user#submitEditUser">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label">Email*</label>
|
||||||
|
<input type="email" name="email" class="form-control" data-user-target="emailInput" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label">Numéro de téléphone</label>
|
||||||
|
<input type="text" name="phoneNumber" class="form-control" data-user-target="phoneInput">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label">Prénom*</label>
|
||||||
|
<input type="text" name="name" class="form-control" data-user-target="nameInput" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label">Nom*</label>
|
||||||
|
<input type="text" name="surname" class="form-control" data-user-target="surnameInput" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="organizationId" value="{{ organizationId }}">
|
||||||
|
<hr>
|
||||||
|
<label class="form-label">**Accès aux applications**</label>
|
||||||
|
<div class="row" data-user-target="appList">
|
||||||
|
{# Checkboxes loaded here #}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
|
||||||
|
<button type="submit" class="btn btn-primary">Enregistrer les modifications</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue