diff --git a/assets/controllers/organization_controller.js b/assets/controllers/organization_controller.js index 063d069..6271eb8 100644 --- a/assets/controllers/organization_controller.js +++ b/assets/controllers/organization_controller.js @@ -5,9 +5,11 @@ import {eyeIconLink, TABULATOR_FR_LANG} from "../js/global.js"; export default class extends Controller { static values = {aws: String, - id: String, - activities: Boolean, - table: Boolean, + id: String, + activities: Boolean, + table: Boolean, + sadmin: Boolean, + user: Number }; static targets = ["activityList", "emptyMessage"] @@ -18,7 +20,7 @@ export default class extends Controller { this.loadActivities(); }, 60000); // Refresh every 60 seconds } - if (this.tableValue){ + if (this.tableValue && this.sadminValue) { this.table(); } @@ -31,7 +33,7 @@ export default class extends Controller { placeholder: "Aucun résultat trouvé pour cette recherche", locale: "fr", //'en' for English, 'fr' for French (en is default, no need to include it) - ajaxURL: "/organization/data", + ajaxURL: `/organization/data/${this.userValue}`, ajaxConfig: "GET", pagination: true, paginationMode: "remote", diff --git a/migrations/Version20260105152103.php b/migrations/Version20260105152103.php new file mode 100644 index 0000000..067cc16 --- /dev/null +++ b/migrations/Version20260105152103.php @@ -0,0 +1,32 @@ +addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_EMAIL ON organizations (email)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('DROP INDEX UNIQ_IDENTIFIER_EMAIL'); + } +} diff --git a/migrations/Version20260106080636.php b/migrations/Version20260106080636.php new file mode 100644 index 0000000..3f3a5e2 --- /dev/null +++ b/migrations/Version20260106080636.php @@ -0,0 +1,32 @@ +addSql('CREATE UNIQUE INDEX UNIQ_ORGANIZATION_EMAIL ON organizations (email)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('DROP INDEX UNIQ_ORGANIZATION_EMAIL'); + } +} diff --git a/migrations/Version20260106084653.php b/migrations/Version20260106084653.php new file mode 100644 index 0000000..ab52dc6 --- /dev/null +++ b/migrations/Version20260106084653.php @@ -0,0 +1,32 @@ +addSql('ALTER TABLE organizations ALTER logo_url DROP NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE organizations ALTER logo_url SET NOT NULL'); + } +} diff --git a/src/Controller/OrganizationController.php b/src/Controller/OrganizationController.php index 2988072..ac8c076 100644 --- a/src/Controller/OrganizationController.php +++ b/src/Controller/OrganizationController.php @@ -16,7 +16,10 @@ use App\Service\LoggerService; use App\Service\OrganizationsService; use App\Service\UserOrganizationService; use App\Service\UserService; +use Doctrine\DBAL\Exception\NonUniqueFieldNameException; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\NonUniqueResultException; use Exception; use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -48,15 +51,19 @@ class OrganizationController extends AbstractController public function index(): Response { $this->denyAccessUnlessGranted('ROLE_ADMIN'); + $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); + if($this->userService->hasAccessTo($actingUser, true)){ + $orgCount = $this->organizationsRepository->count(['isDeleted' => false]); - $orgCount = $this->organizationsRepository->count(['isDeleted' => false]); - - return $this->render('organization/index.html.twig', [ - 'hasOrganizations' => $orgCount > 0 - ]); + return $this->render('organization/index.html.twig', [ + 'hasOrganizations' => $orgCount > 0 + ]); + } + $this->loggerService->logAccessDenied($actingUser->getId()); + throw new AccessDeniedHttpException('Access denied'); } - #[Route(path: '/new', name: 'new', methods: ['GET', 'POST'])] + #[Route(path: '/create', name: 'create', methods: ['GET', 'POST'])] public function new(Request $request): Response { $this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN'); @@ -79,6 +86,7 @@ class OrganizationController extends AbstractController return $this->redirectToRoute('organization_index'); } catch (Exception $e) { $this->addFlash('error', 'Error creating organization: ' . $e->getMessage()); + $this->loggerService->logError('Error creating organization', ['acting_user_id' => $actingUser->getId(), 'error' => $e->getMessage()]); } } return $this->render('organization/new.html.twig', [ @@ -146,7 +154,7 @@ class OrganizationController extends AbstractController } $this->actionService->createAction("Edit Organization", $actingUser, $organization, $organization->getName()); return $this->redirectToRoute('organization_index'); - } catch (Exception $e) { + }catch (Exception $e) { $this->addFlash('error', 'Error editing organization: ' . $e->getMessage()); } } @@ -266,7 +274,7 @@ class OrganizationController extends AbstractController } // API endpoint to fetch organization data for Tabulator - #[Route(path: '/data', name: 'data', methods: ['GET'])] + #[Route(path: '/data/{id}', name: 'data', methods: ['GET'])] public function data(Request $request): JsonResponse { $this->denyAccessUnlessGranted('ROLE_ADMIN'); diff --git a/src/Entity/Organizations.php b/src/Entity/Organizations.php index bff31ba..883cb74 100644 --- a/src/Entity/Organizations.php +++ b/src/Entity/Organizations.php @@ -4,10 +4,13 @@ namespace App\Entity; use App\Repository\OrganizationsRepository; use Doctrine\Common\Collections\ArrayCollection; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: OrganizationsRepository::class)] +#[ORM\UniqueConstraint(name: 'UNIQ_ORGANIZATION_EMAIL', fields: ['email'])] +#[UniqueEntity(fields: ['email'], message: 'Une organisation avec cet email existe déjà.')] class Organizations { #[ORM\Id] @@ -24,7 +27,7 @@ class Organizations #[ORM\Column(length: 255)] private ?string $address = null; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $logo_url = null; #[ORM\Column(options: ['default' => 'CURRENT_TIMESTAMP'])] diff --git a/src/Entity/User.php b/src/Entity/User.php index ca9d110..8709389 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -9,7 +9,6 @@ use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; #[ORM\Entity(repositoryClass: UserRepository::class)] #[ORM\Table(name: '`user`')] diff --git a/src/Form/OrganizationForm.php b/src/Form/OrganizationForm.php index 571b773..da84a2d 100644 --- a/src/Form/OrganizationForm.php +++ b/src/Form/OrganizationForm.php @@ -17,8 +17,8 @@ class OrganizationForm extends AbstractType $builder ->add('email', EmailType::class, ['required' => true, 'label' => 'Email*']) ->add('name', TextType::class, ['required' => true, 'label' => 'Nom de l\'organisation*']) - ->add('address', TextType::class, ['required' => false, 'label' => 'Adresse']) - ->add('number', TextType::class, ['required' => false, 'label' => 'Numéro de téléphone']) + ->add('address', TextType::class, ['required' => true, 'label' => 'Adresse']) + ->add('number', TextType::class, ['required' => true, 'label' => 'Numéro de téléphone']) ->add('logoUrl', FileType::class, [ 'required' => false, 'label' => 'Logo', diff --git a/templates/organization/edit.html.twig b/templates/organization/edit.html.twig index aa9c754..bd1b225 100644 --- a/templates/organization/edit.html.twig +++ b/templates/organization/edit.html.twig @@ -5,9 +5,6 @@