Compare commits
12 Commits
9d394c34f4
...
c673fcd83b
| Author | SHA1 | Date |
|---|---|---|
|
|
c673fcd83b | |
|
|
f0ae5a8c8a | |
|
|
f6a2159177 | |
|
|
9513067ac5 | |
|
|
52b8ba0a10 | |
|
|
70842d6fe9 | |
|
|
7da97b0d02 | |
|
|
f09dd20d2b | |
|
|
94c0fd7c42 | |
|
|
9d541410c4 | |
|
|
a24849abe3 | |
|
|
83c4c221af |
|
|
@ -78,18 +78,16 @@ export default class extends Controller {
|
|||
// If closed, log it for debugging.
|
||||
try {
|
||||
if (this.eventSource.readyState === EventSource.CLOSED) {
|
||||
console.log('Connection closed. Will retry...');
|
||||
console.log();
|
||||
} else if (this.eventSource.readyState === EventSource.CONNECTING) {
|
||||
console.log('Connection is reconnecting (CONNECTING).');
|
||||
console.log();
|
||||
} else if (this.eventSource.readyState === EventSource.OPEN) {
|
||||
console.log('Connection is open (OPEN).');
|
||||
console.log();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error while checking EventSource state:', e);
|
||||
}
|
||||
};
|
||||
|
||||
console.log('EventSource connected to:', url.toString());
|
||||
} catch (error) {
|
||||
console.error('Failed to connect to Mercure:', error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,22 @@ export default class extends Controller {
|
|||
ajaxResponse: (url, params, response) => response,
|
||||
paginationDataSent: { page: "page", size: "size" },
|
||||
paginationDataReceived: { last_page: "last_page" },
|
||||
filterMode: "remote",
|
||||
|
||||
ajaxURLGenerator: function(url, config, params) {
|
||||
let queryParams = new URLSearchParams();
|
||||
queryParams.append('page', params.page || 1);
|
||||
queryParams.append('size', params.size || 10);
|
||||
|
||||
// Add filters
|
||||
if (params.filter) {
|
||||
params.filter.forEach(filter => {
|
||||
queryParams.append(`filter[${filter.field}]`, filter.value);
|
||||
});
|
||||
}
|
||||
|
||||
return `${url}?${queryParams.toString()}`;
|
||||
},
|
||||
|
||||
ajaxSorting: true,
|
||||
ajaxFiltering: true,
|
||||
|
|
|
|||
|
|
@ -203,14 +203,16 @@ export default class extends Controller {
|
|||
e.preventDefault();
|
||||
const userId = target.getAttribute('data-id');
|
||||
if (confirm('Voulez-vous vraiment désactiver cet utilisateur ?')) {
|
||||
const formData = new FormData();
|
||||
formData.append('status', 'deactivate');
|
||||
|
||||
fetch(`/user/deactivate/${userId}`, {
|
||||
fetch(`/user/activeStatus/${userId}`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'}
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (response.ok) {
|
||||
// Option 1: update row status and re-render to switch icon
|
||||
const data = cell.getRow().getData();
|
||||
data.statut = false;
|
||||
cell.getRow().reformat();
|
||||
|
|
@ -228,9 +230,12 @@ export default class extends Controller {
|
|||
e.preventDefault();
|
||||
const userId = target.getAttribute('data-id');
|
||||
if (confirm('Voulez-vous réactiver cet utilisateur ?')) {
|
||||
const formData = new FormData();
|
||||
formData.append('status','activate');
|
||||
|
||||
fetch(`/user/activate/${userId}`, {
|
||||
fetch(`/user/activeStatus/${userId}`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'}
|
||||
})
|
||||
.then(async (response) => {
|
||||
|
|
@ -699,9 +704,10 @@ export default class extends Controller {
|
|||
const userId = target.getAttribute('data-id');
|
||||
if (confirm('Voulez-vous vraiment désactiver cet utilisateur ?')) {
|
||||
const formData = new FormData();
|
||||
formData.append('status', 'deactivate');
|
||||
formData.append('organizationId', target.getAttribute('data-org-id'));
|
||||
|
||||
fetch(`/user/organization/deactivate/${userId}`, {
|
||||
fetch(`/user/organization/activateStatus/${userId}`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'}
|
||||
|
|
@ -726,9 +732,10 @@ export default class extends Controller {
|
|||
const userId = target.getAttribute('data-id');
|
||||
if (confirm('Voulez-vous réactiver cet utilisateur ?')) {
|
||||
const formData = new FormData();
|
||||
formData.append('status', 'activate');
|
||||
formData.append('organizationId', target.getAttribute('data-org-id'));
|
||||
|
||||
fetch(`/user/organization/activate/${userId}`, {
|
||||
fetch(`/user/organization/activateStatus/${userId}`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'}
|
||||
|
|
@ -752,7 +759,7 @@ export default class extends Controller {
|
|||
// columns.push(
|
||||
// {
|
||||
// title: "Statut", field: "role", // or any field you want
|
||||
// headerSort: false,
|
||||
// headerSort: false,x
|
||||
// hozAlign: "center",
|
||||
// vertAlign: "middle",
|
||||
// formatter: (cell) => {
|
||||
|
|
@ -806,6 +813,24 @@ export default class extends Controller {
|
|||
|
||||
ajaxSorting: true,
|
||||
ajaxFiltering: true,
|
||||
filterMode: "remote",
|
||||
|
||||
ajaxURLGenerator: function(url, config, params) {
|
||||
let queryParams = new URLSearchParams();
|
||||
// console.log("orgId:", params.orgId);
|
||||
queryParams.append('orgId', params.orgId);
|
||||
queryParams.append('page', params.page || 1);
|
||||
queryParams.append('size', params.size || 10);
|
||||
|
||||
// Add filters
|
||||
if (params.filter) {
|
||||
params.filter.forEach(filter => {
|
||||
queryParams.append(`filter[${filter.field}]`, filter.value);
|
||||
});
|
||||
}
|
||||
|
||||
return `${url}?${queryParams.toString()}`;
|
||||
},
|
||||
rowHeight: 60,
|
||||
layout: "fitColumns", // activate French
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ parameters:
|
|||
aws_url: '%env(AWS_ENDPOINT)%'
|
||||
aws_public_url: '%env(AWS_ENDPOINT)%'
|
||||
logos_directory: '%kernel.project_dir%/public/uploads/logos'
|
||||
profile_directory: '%kernel.project_dir%/public/uploads/profile'
|
||||
|
||||
services:
|
||||
# default configuration for services in *this* file
|
||||
|
|
@ -29,9 +28,6 @@ services:
|
|||
App\Service\AwsService:
|
||||
arguments:
|
||||
$awsPublicUrl: '%aws_public_url%'
|
||||
App\Service\UserService:
|
||||
arguments:
|
||||
$profileDirectory: '%profile_directory%'
|
||||
App\Service\OrganizationsService:
|
||||
arguments:
|
||||
$logoDirectory: '%logos_directory%'
|
||||
|
|
|
|||
|
|
@ -273,8 +273,7 @@ class OrganizationController extends AbstractController
|
|||
$page = max(1, (int)$request->query->get('page', 1));
|
||||
$size = max(1, (int)$request->query->get('size', 10));
|
||||
|
||||
// $sorters = $request->query->all('sorters');
|
||||
// $filters = $request->query->all('filters');
|
||||
$filters = $request->query->all('filter');
|
||||
|
||||
|
||||
$user = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
||||
|
|
@ -282,25 +281,14 @@ class OrganizationController extends AbstractController
|
|||
$qb = $this->organizationsRepository->createQueryBuilder('o')
|
||||
->where('o.isDeleted = :del')->setParameter('del', false);
|
||||
|
||||
// // Example: apply filters (basic equals/like)
|
||||
// foreach ($filters as $f) {
|
||||
// if (!isset($f['field'], $f['type'])) { continue; }
|
||||
// $param = 'p_' . $f['field'];
|
||||
// if ($f['type'] === 'like' || $f['type'] === 'contains') {
|
||||
// $qb->andWhere("LOWER(o.{$f['field']}) LIKE :$param")
|
||||
// ->setParameter($param, '%' . mb_strtolower((string)$f['value']) . '%');
|
||||
// } elseif ($f['type'] === '=') {
|
||||
// $qb->andWhere("o.{$f['field']} = :$param")
|
||||
// ->setParameter($param, $f['value']);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Example: apply sorters
|
||||
// foreach ($sorters as $s) {
|
||||
// if (!isset($s['field'], $s['dir'])) { continue; }
|
||||
// $dir = strtolower($s['dir']) === 'desc' ? 'DESC' : 'ASC';
|
||||
// $qb->addOrderBy('o.' . $s['field'], $dir);
|
||||
// }
|
||||
if (!empty($filters['name'])) {
|
||||
$qb->andWhere('o.name LIKE :name')
|
||||
->setParameter('name', '%' . $filters['name'] . '%');
|
||||
}
|
||||
if (!empty($filters['email'])) {
|
||||
$qb->andWhere('o.email LIKE :email')
|
||||
->setParameter('email', '%' . $filters['email'] . '%');
|
||||
}
|
||||
|
||||
// Count total
|
||||
$countQb = clone $qb;
|
||||
|
|
|
|||
|
|
@ -21,12 +21,14 @@ use App\Service\UserOrganizationAppService;
|
|||
use App\Service\UserOrganizationService;
|
||||
use App\Service\UserService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use mysql_xdevapi\Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\Mailer\Mailer;
|
||||
use Symfony\Component\Mailer\MailerInterface;
|
||||
use Symfony\Component\Mime\Email;
|
||||
|
|
@ -159,7 +161,6 @@ class UserController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
//TODO : MONOLOG
|
||||
#[Route('/edit/{id}', name: 'edit', methods: ['GET', 'POST'])]
|
||||
public function edit(int $id, Request $request): Response
|
||||
{
|
||||
|
|
@ -183,16 +184,19 @@ class UserController extends AbstractController
|
|||
$user->setModifiedAt(new \DateTimeImmutable('now'));
|
||||
$this->entityManager->persist($user);
|
||||
$this->entityManager->flush();
|
||||
|
||||
//log and action
|
||||
$this->logger->notice("User information edited for " . $user->getUserIdentifier());
|
||||
if ($request->get('organizationId')) {
|
||||
$org = $this->organizationRepository->find($request->get('organizationId'));
|
||||
if ($org) {
|
||||
$this->actionService->createAction("Edit user information", $actingUser, $org, $user->getUserIdentifier());
|
||||
return $this->redirectToRoute('user_show', ['id' => $user->getId(), 'organizationId' => $request->get('organizationId')]);
|
||||
}
|
||||
} else {
|
||||
$this->actionService->createAction("Edit user information", $actingUser, null, $user->getUserIdentifier());
|
||||
return $this->redirectToRoute('user_show', ['id' => $user->getId()]);
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('user_show', ['id' => $user->getId(), 'organizationId' => $request->get('organizationId')]);
|
||||
}
|
||||
|
||||
return $this->render('user/edit.html.twig', [
|
||||
|
|
@ -215,45 +219,30 @@ class UserController extends AbstractController
|
|||
$form = $this->createForm(UserForm::class, $user);
|
||||
$form->handleRequest($request);
|
||||
$orgId = $request->get('organizationId');
|
||||
if ($orgId){
|
||||
$org = $this->organizationRepository->find($orgId) ?? throw new NotFoundHttpException(sprintf('%s not found', $orgId));
|
||||
}
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$existingUser = $this->userRepository->findOneBy(['email' => $user->getEmail()]);
|
||||
if ($existingUser && $orgId) {
|
||||
$org = $this->organizationRepository->find($orgId);
|
||||
$uo = new UsersOrganizations();
|
||||
$uo->setUsers($existingUser);
|
||||
$uo->setOrganization($org);
|
||||
$uo->setStatut("INVITED");
|
||||
$uo->setIsActive(false);
|
||||
$uo->setModifiedAt(new \DateTimeImmutable('now'));
|
||||
$this->entityManager->persist($uo);
|
||||
$this->entityManager->flush();
|
||||
$this->userService->handleExistingUser($existingUser, $org);
|
||||
|
||||
$this->actionService->createAction("Create new user", $existingUser, $org, "Added user to organization" . $existingUser->getUserIdentifier() . " for organization " . $org->getName());
|
||||
$this->logger->notice("User added to organization " . $org->getName());
|
||||
$this->emailService->sendExistingUserNotificationEmail($existingUser, $org);
|
||||
$this->logger->notice("Existing user notification email sent to " . $existingUser->getUserIdentifier());
|
||||
$data = ['user' => $uo->getUsers(), 'organization' => $uo->getOrganization()];
|
||||
$data = ['user' => $existingUser, 'organization' => $org];
|
||||
$this->organizationsService->notifyOrganizationAdmins($data, 'USER_INVITED');
|
||||
|
||||
return $this->redirectToRoute('organization_show', ['id' => $orgId]);
|
||||
}
|
||||
|
||||
// capitalize name and surname
|
||||
$user->setName(ucfirst(strtolower($user->getName())));
|
||||
$user->setSurname(ucfirst(strtolower($user->getSurname())));
|
||||
|
||||
// Handle file upload
|
||||
$picture = $form->get('pictureUrl')->getData();
|
||||
$this->userService->formatNewUserData($user, $picture);
|
||||
|
||||
if ($picture) {
|
||||
$this->userService->handleProfilePicture($user, $picture);
|
||||
}
|
||||
|
||||
//FOR TEST PURPOSES, SETTING A DEFAULT RANDOM PASSWORD
|
||||
$user->setPassword($this->userService->generateRandomPassword());
|
||||
if ($orgId) {
|
||||
$org = $this->organizationRepository->find($orgId);
|
||||
if ($org) {
|
||||
$uo = new UsersOrganizations();
|
||||
$uo->setUsers($user);
|
||||
$uo->setOrganization($org);
|
||||
|
|
@ -267,7 +256,6 @@ class UserController extends AbstractController
|
|||
$this->logger->notice("Password setup email sent to " . $user->getUserIdentifier());
|
||||
$data = ['user' => $uo->getUsers(), 'organization' => $uo->getOrganization()];
|
||||
$this->organizationsService->notifyOrganizationAdmins($data, 'USER_INVITED');
|
||||
}
|
||||
}
|
||||
$this->actionService->createAction("Create new user", $actingUser, null, $user->getUserIdentifier());
|
||||
$this->logger->notice("User created " . $user->getUserIdentifier());
|
||||
|
|
@ -294,127 +282,95 @@ class UserController extends AbstractController
|
|||
}
|
||||
}
|
||||
|
||||
//TODO : MONOLOG
|
||||
#[Route('/deactivate/{id}', name: 'deactivate', methods: ['GET', 'POST'])]
|
||||
public function deactivate(int $id): Response
|
||||
|
||||
#[Route('/activeStatus/{id}', name: 'active_status', methods: ['GET', 'POST'])]
|
||||
public function activeStatus(int $id, Request $request): JsonResponse
|
||||
{
|
||||
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
||||
$actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
||||
if ($this->userService->hasAccessTo($actingUser, true)) {
|
||||
$user = $this->userRepository->find($id);
|
||||
if (!$user) {
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
$user->setIsActive(false);
|
||||
$user->setModifiedAt(new \DateTimeImmutable('now'));
|
||||
$this->userOrganizationService->deactivateAllUserOrganizationLinks($user, $actingUser);
|
||||
if ($this->userService->isUserConnected($user->getUserIdentifier())) {
|
||||
$this->userService->revokeUserTokens($user->getUserIdentifier());
|
||||
}
|
||||
$this->entityManager->persist($user);
|
||||
$this->entityManager->flush();
|
||||
$this->actionService->createAction("Deactivate user", $actingUser, null, $user->getUserIdentifier());
|
||||
try{
|
||||
if ($this->userService->hasAccessTo($actingUser, true)) {
|
||||
$user = $this->userRepository->find($id);
|
||||
if (!$user) {
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
$status = $request->get('status');
|
||||
if ($status === 'deactivate') {
|
||||
$user->setIsActive(false);
|
||||
$this->userOrganizationService->deactivateAllUserOrganizationLinks($actingUser, $user);
|
||||
if ($this->userService->isUserConnected($user->getUserIdentifier())) {
|
||||
$this->userService->revokeUserTokens($user->getUserIdentifier());
|
||||
}
|
||||
$user->setModifiedAt(new \DateTimeImmutable('now'));
|
||||
$this->entityManager->persist($user);
|
||||
$this->entityManager->flush();
|
||||
$this->logger->notice("User deactivated " . $user->getUserIdentifier());
|
||||
$this->actionService->createAction("Deactivate user", $actingUser, null, $user->getUserIdentifier());
|
||||
return new JsonResponse(['status' => 'deactivated'], Response::HTTP_OK);
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('user_index');
|
||||
if ($status === 'activate') {
|
||||
$user->setIsActive(true);
|
||||
$user->setModifiedAt(new \DateTimeImmutable('now'));
|
||||
$this->logger->notice("User activated " . $user->getUserIdentifier());
|
||||
$this->actionService->createAction("Activate user", $actingUser, null, $user->getUserIdentifier());
|
||||
return new JsonResponse(['status' => 'activated'], Response::HTTP_OK);
|
||||
}
|
||||
}
|
||||
}catch (\Exception $e){
|
||||
$this->logger->error($e->getMessage());
|
||||
}
|
||||
|
||||
throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
|
||||
//TODO : MONOLOG
|
||||
#[Route('/activate/{id}', name: 'activate', methods: ['GET', 'POST'])]
|
||||
public function activate(int $id): Response
|
||||
{
|
||||
#[Route('/organization/activateStatus/{id}', name: 'activate_organization', methods: ['GET', 'POST'])]
|
||||
public function activateStatusOrganization(int $id, Request $request): JsonResponse{
|
||||
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
||||
$actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
||||
if ($this->userService->hasAccessTo($actingUser, true)) {
|
||||
$user = $this->userRepository->find($id);
|
||||
if (!$user) {
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
try {
|
||||
if ($this->userService->hasAccessTo($actingUser, true)) {
|
||||
$orgId = $request->get('organizationId');
|
||||
$org = $this->organizationRepository->find($orgId);
|
||||
if (!$org) {
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
$user = $this->userRepository->find($id);
|
||||
if (!$user) {
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
$uo = $this->uoRepository->findOneBy(['users' => $user,
|
||||
'organization' => $org]);
|
||||
if (!$uo) {
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
$status = $request->get('status');
|
||||
if ($status === 'deactivate') {
|
||||
$uo->setIsActive(false);
|
||||
$this->userOrganizationAppService->deactivateAllUserOrganizationsAppLinks($uo);
|
||||
$this->entityManager->persist($uo);
|
||||
$this->entityManager->flush();
|
||||
$data = ['user' => $user,
|
||||
'organization' => $org];
|
||||
$this->organizationsService->notifyOrganizationAdmins($data, "USER_DEACTIVATED");
|
||||
$this->logger->notice("User Organizaton deactivated " . $user->getUserIdentifier());
|
||||
$this->actionService->createAction("Deactivate user in organization", $actingUser, $org, $org->getName() . " for user " . $user->getUserIdentifier());
|
||||
return new JsonResponse(['status' => 'deactivated'], Response::HTTP_OK);
|
||||
}
|
||||
if($status === "activate"){
|
||||
$uo->setIsActive(true);
|
||||
$this->entityManager->persist($uo);
|
||||
$this->entityManager->flush();
|
||||
$this->actionService->createAction("Activate user in organization", $actingUser, $org, $org->getName() . " for user " . $user->getUserIdentifier());
|
||||
$data = ['user' => $user,
|
||||
'organization' => $org];
|
||||
$this->organizationsService->notifyOrganizationAdmins($data, "USER_ACTIVATED");
|
||||
return new JsonResponse(['status' => 'activated'], Response::HTTP_OK);
|
||||
}
|
||||
}
|
||||
$user->setIsActive(true);
|
||||
$user->setModifiedAt(new \DateTimeImmutable('now'));
|
||||
$this->entityManager->persist($user);
|
||||
$this->entityManager->flush();
|
||||
$this->actionService->createAction("Activate user", $actingUser, null, $user->getUserIdentifier());
|
||||
|
||||
return $this->redirectToRoute('user_index');
|
||||
}catch (\Exception $exception){
|
||||
$this->logger->error($exception->getMessage());
|
||||
}
|
||||
|
||||
throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
//TODO : MONOLOG
|
||||
#[Route('/organization/deactivate/{id}', name: 'deactivate_organization', methods: ['GET', 'POST'])]
|
||||
public function deactivateUserInOrganization(int $id, Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
||||
$actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
||||
if ($this->userService->hasAccessTo($actingUser, true)) {
|
||||
$orgId = $request->get('organizationId');
|
||||
$org = $this->organizationRepository->find($orgId);
|
||||
if (!$org) {
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
$user = $this->userRepository->find($id);
|
||||
if (!$user) {
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
$uo = $this->uoRepository->findOneBy(['users' => $user,
|
||||
'organization' => $org,
|
||||
'isActive' => true]);
|
||||
if (!$uo) {
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
$uo->setIsActive(false);
|
||||
$this->userOrganizationAppService->deactivateAllUserOrganizationsAppLinks($uo);
|
||||
$data = ['user' => $user,
|
||||
'organization' => $org];
|
||||
$this->organizationsService->notifyOrganizationAdmins($data, "USER_DEACTIVATED");
|
||||
$this->entityManager->persist($uo);
|
||||
$this->entityManager->flush();
|
||||
$this->actionService->createAction("Deactivate user in organization", $actingUser, $org, $org->getName() . " for user " . $user->getUserIdentifier());
|
||||
|
||||
return new Response('', Response::HTTP_NO_CONTENT); //204
|
||||
}
|
||||
|
||||
throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
//TODO : MONOLOG
|
||||
#[Route('/organization/activate/{id}', name: 'activate_organization', methods: ['GET', 'POST'])]
|
||||
public function activateUserInOrganization(int $id, Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
||||
$actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
||||
if ($this->userService->hasAccessTo($actingUser, true)) {
|
||||
$orgId = $request->get('organizationId');
|
||||
$org = $this->organizationRepository->find($orgId);
|
||||
if (!$org) {
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
$user = $this->userRepository->find($id);
|
||||
if (!$user) {
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
$uo = $this->uoRepository->findOneBy(['users' => $user,
|
||||
'organization' => $org,
|
||||
'isActive' => false]);
|
||||
if (!$uo) {
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
$uo->setIsActive(true);
|
||||
$this->entityManager->persist($uo);
|
||||
$this->entityManager->flush();
|
||||
$this->actionService->createAction("Activate user in organization", $actingUser, $org, $org->getName() . " for user " . $user->getUserIdentifier());
|
||||
$data = ['user' => $user,
|
||||
'organization' => $org];
|
||||
$this->organizationsService->notifyOrganizationAdmins($data, "USER_ACTIVATED");
|
||||
|
||||
return $this->redirectToRoute('user_index');
|
||||
}
|
||||
|
||||
throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
}
|
||||
|
||||
//TODO : MONOLOG + remove picture from bucket
|
||||
|
|
@ -429,7 +385,7 @@ class UserController extends AbstractController
|
|||
}
|
||||
$user->setIsActive(false);
|
||||
$user->setModifiedAt(new \DateTimeImmutable('now'));
|
||||
$this->userOrganizationService->deactivateAllUserOrganizationLinks($user, $actingUser);
|
||||
$this->userOrganizationService->deactivateAllUserOrganizationLinks($actingUser, $user);
|
||||
$user->setIsDeleted(true);
|
||||
if ($this->userService->isUserConnected($user)) {
|
||||
$this->userService->revokeUserTokens($user->getUserIdentifier());
|
||||
|
|
@ -663,16 +619,30 @@ class UserController extends AbstractController
|
|||
$page = max(1, (int)$request->query->get('page', 1));
|
||||
$size = max(1, (int)$request->query->get('size', 10));
|
||||
|
||||
// Optional: read Tabulator remote sort/filter payloads
|
||||
// $sorters = $request->query->all('sorters') ?? [];
|
||||
// $filters = $request->query->all('filters') ?? [];
|
||||
$filters = $request->query->all('filter') ?? [];
|
||||
|
||||
$repo = $this->uoRepository;
|
||||
|
||||
// Base query
|
||||
$qb = $repo->createQueryBuilder('uo')
|
||||
->join('uo.users', 'u')
|
||||
->where('uo.organization = :orgId')
|
||||
->setParameter('orgId', $orgId);
|
||||
|
||||
// Apply filters
|
||||
if (!empty($filters['name'])) {
|
||||
$qb->andWhere('u.surname LIKE :name')
|
||||
->setParameter('name', '%' . $filters['name'] . '%');
|
||||
}
|
||||
if (!empty($filters['prenom'])) {
|
||||
$qb->andWhere('u.name LIKE :prenom')
|
||||
->setParameter('prenom', '%' . $filters['prenom'] . '%');
|
||||
}
|
||||
if (!empty($filters['email'])) {
|
||||
$qb->andWhere('u.email LIKE :email')
|
||||
->setParameter('email', '%' . $filters['email'] . '%');
|
||||
}
|
||||
|
||||
$countQb = clone $qb;
|
||||
$total = (int)$countQb->select('COUNT(uo.id)')->getQuery()->getSingleScalarResult();
|
||||
|
||||
|
|
|
|||
|
|
@ -26,16 +26,21 @@ readonly class UserOrganizationService
|
|||
/**
|
||||
* Deactive all user organization links.
|
||||
*
|
||||
* @param User $user
|
||||
* @param User $actingUser
|
||||
* @param User|null $user
|
||||
* @param Organizations|null $organizations
|
||||
* @return void
|
||||
*/
|
||||
public function deactivateAllUserOrganizationLinks(User $actingUser, User $user = null, Organizations $organizations = null): void{
|
||||
//If user provided, get all UO links for that user that are active
|
||||
if($user !== null) {
|
||||
$uos = $this->entityManager->getRepository(UsersOrganizations::class)->findBy(['users' => $user, 'isActive' => true]);
|
||||
}elseif($organizations !== null){
|
||||
}
|
||||
// if organization provided, get all UO links for that organization that are active
|
||||
elseif($organizations !== null){
|
||||
$uos = $this->entityManager->getRepository(UsersOrganizations::class)->findBy(['organization' => $organizations, 'isActive' => true]);
|
||||
}
|
||||
//deactivate all UO links
|
||||
foreach ($uos as $uo) {
|
||||
$this->userOrganizationAppService->deactivateAllUserOrganizationsAppLinks($uo);
|
||||
$uo->setIsActive(false);
|
||||
|
|
|
|||
|
|
@ -24,14 +24,13 @@ class UserService
|
|||
{
|
||||
|
||||
public const NOT_FOUND = 'Entity not found';
|
||||
private string $profileDirectory;
|
||||
|
||||
public function __construct(private readonly EntityManagerInterface $entityManager,
|
||||
private readonly Security $security,
|
||||
string $profileDirectory, private readonly AwsService $awsService
|
||||
private readonly AwsService $awsService
|
||||
)
|
||||
{
|
||||
$this->profileDirectory = $profileDirectory;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -133,35 +132,6 @@ class UserService
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Organizations id where the user is admin
|
||||
*
|
||||
* @param User $user
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getAdminOrganizationsIds(User $user): array
|
||||
{
|
||||
$orgIds = [];
|
||||
try {
|
||||
$uo = $this->entityManager->getRepository(UsersOrganizations::class)->findBy(['users' => $user]);
|
||||
$roleAdmin = $this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'ADMIN']);
|
||||
if ($uo) {
|
||||
foreach ($uo as $u) {
|
||||
$uoa = $this->entityManager->getRepository(UserOrganizatonApp::class)->findOneBy(['userOrganization' => $u,
|
||||
'role' => $roleAdmin,
|
||||
'isActive' => true]);
|
||||
if ($uoa && $this->security->isGranted('ROLE_ADMIN')) {
|
||||
$orgIds[] = $u->getOrganization()->getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (EntityNotFoundException $e) {
|
||||
throw new EntityNotFoundException("Error while fetching organizations ids where the user is admin");
|
||||
}
|
||||
|
||||
return array_unique($orgIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user by their identifier.
|
||||
|
|
@ -179,45 +149,6 @@ class UserService
|
|||
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 ($usersOrganizations as $userOrg) {
|
||||
$org = $userOrg->getOrganization();
|
||||
if (!$org) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$orgId = $org->getId();
|
||||
$orgName = $org->getName();
|
||||
$orgLogo = $org->getLogoUrl();
|
||||
|
||||
if (!isset($grouped[$orgId])) {
|
||||
$grouped[$orgId] = [
|
||||
'id' => $orgId,
|
||||
'name' => $orgName,
|
||||
'logo' => $orgLogo,
|
||||
'users' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$user = $userOrg->getUsers();
|
||||
$grouped[$orgId]['users'][] = [
|
||||
'entity' => $user,
|
||||
'connected' => $this->isUserConnected($user->getUserIdentifier()),
|
||||
'isActive' => (bool)$userOrg->isActive()
|
||||
];
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format users without organization for admin view.
|
||||
|
|
@ -243,6 +174,7 @@ class UserService
|
|||
// Use a fixed key (e.g., 0 or 'none') to avoid collisions with real org IDs
|
||||
return ['none' => $group];
|
||||
}
|
||||
|
||||
//TODO: reset function
|
||||
public function handleProfilePicture(User $user, $picture): void
|
||||
{
|
||||
|
|
@ -252,10 +184,10 @@ class UserService
|
|||
// Create custom filename: userNameUserSurname_ddmmyyhhmmss
|
||||
$customFilename = $user->getName() . $user->getSurname() . '_' . date('dmyHis') . '.' . $extension;
|
||||
// $customFilename = $user->getName() . $user->getSurname() . "." .$extension;
|
||||
try{
|
||||
$this->awsService->PutDocObj($_ENV['S3_PORTAL_BUCKET'], $picture, $customFilename , $extension, 'profile/');
|
||||
try {
|
||||
$this->awsService->PutDocObj($_ENV['S3_PORTAL_BUCKET'], $picture, $customFilename, $extension, 'profile/');
|
||||
|
||||
$user->setPictureUrl('profile/'.$customFilename);
|
||||
$user->setPictureUrl('profile/' . $customFilename);
|
||||
} catch (FileException $e) {
|
||||
// Handle upload error
|
||||
throw new FileException('File upload failed: ' . $e->getMessage());
|
||||
|
|
@ -355,7 +287,7 @@ class UserService
|
|||
return 'ROLE_' . $role;
|
||||
}
|
||||
|
||||
public function revokeUserTokens(String $userIdentifier)
|
||||
public function revokeUserTokens(string $userIdentifier)
|
||||
{
|
||||
$tokens = $this->entityManager->getRepository(AccessToken::class)->findBy([
|
||||
'userIdentifier' => $userIdentifier,
|
||||
|
|
@ -398,7 +330,7 @@ class UserService
|
|||
return $formatted;
|
||||
}
|
||||
|
||||
public function generatePasswordToken(User $user, int $orgId): string
|
||||
public function generatePasswordToken(User $user, int $orgId): string
|
||||
{
|
||||
$orgString = "o" . $orgId . "@";
|
||||
$token = $orgString . bin2hex(random_bytes(32));
|
||||
|
|
@ -417,7 +349,7 @@ class UserService
|
|||
return true;
|
||||
}
|
||||
|
||||
public function isPasswordStrong(string $newPassword):bool
|
||||
public function isPasswordStrong(string $newPassword): bool
|
||||
{
|
||||
$pewpew = 0;
|
||||
if (preg_match('/\w/', $newPassword)) { //Find any alphabetical letter (a to Z) and digit (0 to 9)
|
||||
|
|
@ -493,4 +425,57 @@ class UserService
|
|||
return $isAdmin && !empty($org);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle already existing user when creating a user.
|
||||
* If the user exists but is inactive, reactivate them.
|
||||
*
|
||||
* @param User $user
|
||||
* @param Organizations $organization
|
||||
* @return void
|
||||
*/
|
||||
public function handleExistingUser(User $user, Organizations $organization): void
|
||||
{
|
||||
if (!$user->isActive()) {
|
||||
$user->setIsActive(true);
|
||||
$this->entityManager->persist($user);
|
||||
}
|
||||
$uo = new UsersOrganizations();
|
||||
$uo->setUsers($user);
|
||||
$uo->setOrganization($organization);
|
||||
$uo->setStatut("INVITED");
|
||||
$uo->setIsActive(false);
|
||||
$uo->setModifiedAt(new \DateTimeImmutable('now'));
|
||||
$this->entityManager->persist($uo);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format new user data.
|
||||
* Capitalize name and surname.
|
||||
* Trim strings.
|
||||
* Set random password
|
||||
* Handle picture if provided
|
||||
*
|
||||
* @param User $user
|
||||
* @return void
|
||||
*/
|
||||
public function formatNewUserData(User $user, $picture): void
|
||||
{
|
||||
// capitalize name and surname
|
||||
$user->setName(ucfirst(strtolower($user->getName())));
|
||||
$user->setSurname(ucfirst(strtolower($user->getSurname())));
|
||||
|
||||
// trim strings
|
||||
$user->setName(trim($user->getName()));
|
||||
$user->setSurname(trim($user->getSurname()));
|
||||
$user->setEmail(trim($user->getEmail()));
|
||||
|
||||
//FOR SETTING A DEFAULT RANDOM PASSWORD OF 50 CHARACTERS until user set his own password
|
||||
$user->setPassword($this->generateRandomPassword());
|
||||
|
||||
if($picture) {
|
||||
$this->handleProfilePicture($user, $picture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
{% block body %}
|
||||
<div class="w-100 h-100 p-5 m-auto">
|
||||
<div class="card p-3 m-3">
|
||||
<div class="card no-header-bg p-3 m-3">
|
||||
<div class="card-header border-0">
|
||||
<h2>Modifier l'organisation</h2>
|
||||
{% if is_granted("ROLE_SUPER_ADMIN") %}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
{% block body %}
|
||||
<div class="w-100 h-100 p-5 m-auto">
|
||||
<div class="card p-3 m-3 border-0">
|
||||
<div class="card no-header-bg p-3 m-3 border-0">
|
||||
<div class="card-header d-flex justify-content-between align-items-center border-0">
|
||||
<div class="card-title">
|
||||
<h1>Gestion des organisations</h1>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
{% block body %}
|
||||
<div class="w-100 h-100 p-5 m-auto">
|
||||
<div class="card p-3 m-3">
|
||||
<div class="card no-header-bg p-3 m-3">
|
||||
<div class="card-header border-0">
|
||||
<div class="card-title d-flex justify-content-between align-items-center">
|
||||
<h1>Ajouter une organisation</h1>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
{# User tables #}
|
||||
<div class="col-9">
|
||||
<div class="row mb-3 d-flex gap-2 ">
|
||||
<div class="col mb-3 card">
|
||||
<div class="col mb-3 card no-header-bg">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h2>
|
||||
Nouveaux utilisateurs
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col mb-3 card">
|
||||
<div class="col mb-3 card no-header-bg">
|
||||
<div class="card-header">
|
||||
<h2>
|
||||
Administrateurs
|
||||
|
|
@ -76,7 +76,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3 card">
|
||||
<div class="row mb-3 card no-header-bg">
|
||||
<div class="card-header">
|
||||
<h2>
|
||||
Mes utilisateurs
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
{% block body %}
|
||||
<div class="w-100 h-100 p-5 m-auto">
|
||||
<div class="card p-3">
|
||||
<div class="card p-3 no-header-bg">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div class="card-title">
|
||||
<h2>Modifier l'utilisateur</h2>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
{% block body %}
|
||||
<div class="w-100 h-100 p-5 m-auto">
|
||||
<div class="card p-3">
|
||||
<div class="card no-header-bg p-3">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div class="card-title">
|
||||
<h1>Ajouter un utilisateur</h1>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
{% block body %}
|
||||
|
||||
<div class="w-100 h-100 p-5 m-auto">
|
||||
<div class="card p-3 m-3 border-0">
|
||||
<div class="card p-3 m-3 border-0 no-header-bg">
|
||||
|
||||
{% if is_granted("ROLE_ADMIN") %}
|
||||
<div class="card-header border-0 d-flex justify-content-between align-items-center ">
|
||||
|
|
|
|||
Loading…
Reference in New Issue