diff --git a/.env b/.env index 45d04f9..8867c0c 100644 --- a/.env +++ b/.env @@ -1,10 +1,11 @@ -APP_ENV=dev -APP_SECRET=je_te_remplis_parce_que_tu_me_mets_des_messages_d_erreur -APP_SHARE_DIR=var/share -APP_VERSION=0.0.1 -DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8" -MESSENGER_TRANSPORT_DSN="doctrine://default" -MAILER_DSN="smtp://localhost:1025" -DEFAULT_URI="http://localhost:8000" +APP_ENV= +APP_SECRET= +APP_SHARE_DIR= +APP_VERSION= +DATABASE_URL= +MESSENGER_TRANSPORT_DSN= +MAILER_DSN= +DEFAULT_URI= +KAZ_API_BASE_URL= KAZ_API_USER= KAZ_API_PASSWORD= diff --git a/assets/styles/app.css b/assets/styles/app.css index e6b1010..d760871 100644 --- a/assets/styles/app.css +++ b/assets/styles/app.css @@ -30,6 +30,6 @@ --color-gris-fonce: #4B5563; /* Polices */ - --font-sora: "Sora", system-ui, sans-serif; + --font-sora: "Sora", sans-serif; --font-caveat: "Caveat", cursive; } diff --git a/config/services.yaml b/config/services.yaml index b37d953..421e277 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -16,7 +16,7 @@ services: $apiUser: '%env(KAZ_API_USER)%' $apiPassword: '%env(KAZ_API_PASSWORD)%' - # Gestion de l'enregistrement de la photo de profil + # Gestion de l'enregistrement de l'image de profil App\Service\FileUploader: arguments: $targetDirectory: '%images_directory%' diff --git a/migrations/Version20260313151403.php b/migrations/Version20260313151403.php deleted file mode 100644 index de8ff23..0000000 --- a/migrations/Version20260313151403.php +++ /dev/null @@ -1,35 +0,0 @@ -addSql('CREATE TABLE "user" (id UUID NOT NULL, email VARCHAR(180) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, email_quota VARCHAR(255) NOT NULL, alternate_email VARCHAR(255) NOT NULL, identifiant_kaz VARCHAR(255) NOT NULL, quota VARCHAR(255) NOT NULL, has_nextcloud_access BOOLEAN NOT NULL, nextcloud_quota VARCHAR(255) NOT NULL, has_mobilizon BOOLEAN NOT NULL, has_agora_access BOOLEAN NOT NULL, lastname VARCHAR(255) NOT NULL, firstname VARCHAR(255) NOT NULL, PRIMARY KEY (id))'); - $this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_EMAIL ON "user" (email)'); - $this->addSql('CREATE TABLE messenger_messages (id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, body TEXT NOT NULL, headers TEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, available_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, delivered_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, PRIMARY KEY (id))'); - $this->addSql('CREATE INDEX IDX_75EA56E0FB7336F0E3BD61CE16BA31DBBF396750 ON messenger_messages (queue_name, available_at, delivered_at, id)'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('DROP TABLE "user"'); - $this->addSql('DROP TABLE messenger_messages'); - } -} diff --git a/migrations/Version20260316103235.php b/migrations/Version20260316103235.php deleted file mode 100644 index 4094fd5..0000000 --- a/migrations/Version20260316103235.php +++ /dev/null @@ -1,31 +0,0 @@ -addSql('ALTER TABLE "user" ALTER alternate_email DROP NOT NULL'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE "user" ALTER alternate_email SET NOT NULL'); - } -} diff --git a/migrations/Version20260316104254.php b/migrations/Version20260316104254.php deleted file mode 100644 index baf28d0..0000000 --- a/migrations/Version20260316104254.php +++ /dev/null @@ -1,31 +0,0 @@ -addSql('ALTER TABLE "user" ALTER identifiant_kaz DROP NOT NULL'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE "user" ALTER identifiant_kaz SET NOT NULL'); - } -} diff --git a/migrations/Version20260316104335.php b/migrations/Version20260316104335.php deleted file mode 100644 index ebcb3db..0000000 --- a/migrations/Version20260316104335.php +++ /dev/null @@ -1,31 +0,0 @@ -addSql('ALTER TABLE "user" ALTER quota DROP NOT NULL'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE "user" ALTER quota SET NOT NULL'); - } -} diff --git a/migrations/Version20260316104505.php b/migrations/Version20260316104505.php deleted file mode 100644 index c68c5fd..0000000 --- a/migrations/Version20260316104505.php +++ /dev/null @@ -1,31 +0,0 @@ -addSql('ALTER TABLE "user" ALTER has_agora_access DROP NOT NULL'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE "user" ALTER has_agora_access SET NOT NULL'); - } -} diff --git a/migrations/Version20260316104557.php b/migrations/Version20260316104557.php deleted file mode 100644 index bcd88dc..0000000 --- a/migrations/Version20260316104557.php +++ /dev/null @@ -1,33 +0,0 @@ -addSql('ALTER TABLE "user" ALTER has_nextcloud_access DROP NOT NULL'); - $this->addSql('ALTER TABLE "user" ALTER has_mobilizon DROP NOT NULL'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE "user" ALTER has_nextcloud_access SET NOT NULL'); - $this->addSql('ALTER TABLE "user" ALTER has_mobilizon SET NOT NULL'); - } -} diff --git a/migrations/Version20260316114715.php b/migrations/Version20260316114715.php deleted file mode 100644 index d0ff32d..0000000 --- a/migrations/Version20260316114715.php +++ /dev/null @@ -1,49 +0,0 @@ -addSql('ALTER TABLE "user" ADD last_name VARCHAR(255) NOT NULL'); - $this->addSql('ALTER TABLE "user" ADD first_name VARCHAR(255) NOT NULL'); - $this->addSql('ALTER TABLE "user" DROP lastname'); - $this->addSql('ALTER TABLE "user" DROP firstname'); - $this->addSql('ALTER TABLE "user" ALTER alternate_email SET NOT NULL'); - $this->addSql('ALTER TABLE "user" ALTER identifiant_kaz SET NOT NULL'); - $this->addSql('ALTER TABLE "user" ALTER quota SET NOT NULL'); - $this->addSql('ALTER TABLE "user" ALTER has_nextcloud_access SET NOT NULL'); - $this->addSql('ALTER TABLE "user" ALTER has_mobilizon SET NOT NULL'); - $this->addSql('ALTER TABLE "user" ALTER has_agora_access SET NOT NULL'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE "user" ADD lastname VARCHAR(255) NOT NULL'); - $this->addSql('ALTER TABLE "user" ADD firstname VARCHAR(255) NOT NULL'); - $this->addSql('ALTER TABLE "user" DROP last_name'); - $this->addSql('ALTER TABLE "user" DROP first_name'); - $this->addSql('ALTER TABLE "user" ALTER alternate_email DROP NOT NULL'); - $this->addSql('ALTER TABLE "user" ALTER identifiant_kaz DROP NOT NULL'); - $this->addSql('ALTER TABLE "user" ALTER quota DROP NOT NULL'); - $this->addSql('ALTER TABLE "user" ALTER has_nextcloud_access DROP NOT NULL'); - $this->addSql('ALTER TABLE "user" ALTER has_mobilizon DROP NOT NULL'); - $this->addSql('ALTER TABLE "user" ALTER has_agora_access DROP NOT NULL'); - } -} diff --git a/migrations/Version20260326214353.php b/migrations/Version20260326214353.php deleted file mode 100644 index a2eddd8..0000000 --- a/migrations/Version20260326214353.php +++ /dev/null @@ -1,31 +0,0 @@ -addSql('ALTER TABLE "user" ADD photo VARCHAR(255) DEFAULT NULL'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE "user" DROP photo'); - } -} diff --git a/migrations/Version20260326231417.php b/migrations/Version20260326231417.php deleted file mode 100644 index d7e8987..0000000 --- a/migrations/Version20260326231417.php +++ /dev/null @@ -1,31 +0,0 @@ -addSql('ALTER TABLE "user" ADD telephone VARCHAR(20) DEFAULT NULL'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE "user" DROP telephone'); - } -} diff --git a/migrations/Version20260328101039.php b/migrations/Version20260328101039.php deleted file mode 100644 index 3c11da4..0000000 --- a/migrations/Version20260328101039.php +++ /dev/null @@ -1,35 +0,0 @@ -addSql('CREATE TABLE "user" (id UUID NOT NULL, email VARCHAR(180) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, email_quota VARCHAR(255) NOT NULL, alternate_email VARCHAR(255) NOT NULL, identifiant_kaz VARCHAR(255) NOT NULL, quota VARCHAR(255) NOT NULL, has_nextcloud_access BOOLEAN NOT NULL, nextcloud_quota VARCHAR(255) NOT NULL, has_mobilizon BOOLEAN NOT NULL, has_agora_access BOOLEAN NOT NULL, last_name VARCHAR(255) NOT NULL, first_name VARCHAR(255) NOT NULL, photo VARCHAR(255) DEFAULT NULL, telephone VARCHAR(20) DEFAULT NULL, PRIMARY KEY (id))'); - $this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_EMAIL ON "user" (email)'); - $this->addSql('CREATE TABLE messenger_messages (id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, body TEXT NOT NULL, headers TEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, available_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, delivered_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, PRIMARY KEY (id))'); - $this->addSql('CREATE INDEX IDX_75EA56E0FB7336F0E3BD61CE16BA31DBBF396750 ON messenger_messages (queue_name, available_at, delivered_at, id)'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('DROP TABLE "user"'); - $this->addSql('DROP TABLE messenger_messages'); - } -} diff --git a/migrations/Version20260328101220.php b/migrations/Version20260328101220.php deleted file mode 100644 index d51320f..0000000 --- a/migrations/Version20260328101220.php +++ /dev/null @@ -1,35 +0,0 @@ -addSql('CREATE TABLE "user" (id UUID NOT NULL, email VARCHAR(180) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, email_quota VARCHAR(255) NOT NULL, alternate_email VARCHAR(255) NOT NULL, identifiant_kaz VARCHAR(255) NOT NULL, quota VARCHAR(255) NOT NULL, has_nextcloud_access BOOLEAN NOT NULL, nextcloud_quota VARCHAR(255) NOT NULL, has_mobilizon BOOLEAN NOT NULL, has_agora_access BOOLEAN NOT NULL, last_name VARCHAR(255) NOT NULL, first_name VARCHAR(255) NOT NULL, photo VARCHAR(255) DEFAULT NULL, telephone VARCHAR(20) DEFAULT NULL, PRIMARY KEY (id))'); - $this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_EMAIL ON "user" (email)'); - $this->addSql('CREATE TABLE messenger_messages (id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, body TEXT NOT NULL, headers TEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, available_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, delivered_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, PRIMARY KEY (id))'); - $this->addSql('CREATE INDEX IDX_75EA56E0FB7336F0E3BD61CE16BA31DBBF396750 ON messenger_messages (queue_name, available_at, delivered_at, id)'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('DROP TABLE "user"'); - $this->addSql('DROP TABLE messenger_messages'); - } -} diff --git a/migrations/Version20260329084928.php b/migrations/Version20260331084216.php similarity index 90% rename from migrations/Version20260329084928.php rename to migrations/Version20260331084216.php index aa29b31..8f5111f 100644 --- a/migrations/Version20260329084928.php +++ b/migrations/Version20260331084216.php @@ -10,7 +10,7 @@ use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ -final class Version20260329084928 extends AbstractMigration +final class Version20260331084216 extends AbstractMigration { public function getDescription(): string { @@ -20,8 +20,8 @@ final class Version20260329084928 extends AbstractMigration public function up(Schema $schema): void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE TABLE "user" (id UUID NOT NULL, email VARCHAR(180) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, email_quota VARCHAR(255) NOT NULL, alternate_email VARCHAR(255) NOT NULL, identifiant_kaz VARCHAR(255) NOT NULL, quota VARCHAR(255) NOT NULL, has_nextcloud_access BOOLEAN NOT NULL, nextcloud_quota VARCHAR(255) NOT NULL, has_mobilizon BOOLEAN NOT NULL, has_agora_access BOOLEAN NOT NULL, last_name VARCHAR(255) NOT NULL, first_name VARCHAR(255) NOT NULL, photo VARCHAR(255) DEFAULT NULL, telephone VARCHAR(20) DEFAULT NULL, PRIMARY KEY (id))'); - $this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_EMAIL ON "user" (email)'); + $this->addSql('CREATE TABLE "user" (id UUID NOT NULL, email VARCHAR(180) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, email_quota VARCHAR(255) NOT NULL, alternate_email VARCHAR(255) NOT NULL, identifiant_kaz VARCHAR(255) NOT NULL, quota VARCHAR(255) NOT NULL, has_nextcloud_access BOOLEAN NOT NULL, nextcloud_quota VARCHAR(255) NOT NULL, has_mobilizon BOOLEAN NOT NULL, has_agora_access BOOLEAN NOT NULL, last_name VARCHAR(255) NOT NULL, first_name VARCHAR(255) NOT NULL, image VARCHAR(255) DEFAULT NULL, telephone VARCHAR(20) DEFAULT NULL, PRIMARY KEY (id))'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_8D93D649E7927C74 ON "user" (email)'); $this->addSql('CREATE TABLE messenger_messages (id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, body TEXT NOT NULL, headers TEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, available_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, delivered_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, PRIMARY KEY (id))'); $this->addSql('CREATE INDEX IDX_75EA56E0FB7336F0E3BD61CE16BA31DBBF396750 ON messenger_messages (queue_name, available_at, delivered_at, id)'); } diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 972912f..52e32c9 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -7,10 +7,11 @@ use App\Form\UserProfileType; use App\Service\FileUploader; use App\Service\KazApiService; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\File\UploadedFile; -use Symfony\Component\HttpFoundation\Request; +use Exception; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\FormError; +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use Symfony\Component\Routing\Attribute\Route; @@ -45,8 +46,6 @@ class UserController extends AbstractController 'user' => $user, ]); } - - /* TODO : Param l'API avec un Serializer pour la lecture du fichier JSON ? */ #[Route('/mon-profil', name: 'app_user', methods: ['GET', 'POST'])] #[IsGranted('ROLE_USER')] public function showProfile( @@ -54,66 +53,54 @@ class UserController extends AbstractController EntityManagerInterface $entityManager, FileUploader $fileUploader, KazApiService $apiKazService - ): Response { - # Récupération de l'utilisateur actuellement connecté + ): Response + { + // Récupération de l'utilisateur actuellement connecté $user = $this->getUser(); + // Vérification si l'URL est en mode édition + $isEditMode = $request->query->getBoolean('edit', false); - $kazUser = $apiKazService->getUserData($user->getEmail()); + try { + // Récupération des données de l'utilisateur sur l'API grâce à son email + $kazUser = $apiKazService->getUserData($user->getEmail()); + // Initialisation de la variable $userData + $user = $user->updateFromKazUser($kazUser); + } catch (Exception $e) { + $this->addFlash('error', 'Impossible de charger vos données.'); + } - $user = $user->updateFromKazUser($kazUser); - - //TODO: modifier pour que ça communique avec l'API */ - - # Création du formulaire lié à l'utilisateur connecté + // Création du formulaire lié à l'utilisateur connecté $form = $this->createForm(UserProfileType::class, $user); $form->handleRequest($request); - # Traitement si l'utilisateur clique sur "Valider" + // Affichage du formulaire si les données sont valides if ($form->isSubmitted() && $form->isValid()) { - - /** @var UploadedFile $imageFile */ + /** @var UploadedFile|null $imageFile */ $imageFile = $form->get('image')->getData(); + // --- Gestion de l'image de profil --- if ($imageFile) { - # Suppression de l'ancienne photo du serveur - $fileUploader->delete($user->getPhoto()); - - # Dépot de la nouvelle photo + // Suppression de l'ancienne image via le service + if ($user->getImage()) { + $fileUploader->delete($user->getImage()); + } + // Dépôt de la nouvelle image et mise à jour de son nom dans l'entité $newFilename = $fileUploader->upload($imageFile); - - # Mise à jour de l'utilisateur avec le nouveau nom - $user->setPhoto($newFilename); + $user->setImage($newFilename); } - $alternateEmail = $form->get('alternateEmail')->getData(); - $regexEmail = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/'; - if(isset($alternateEmail) && preg_match($regexEmail, $alternateEmail)) { - $user->setAlternateEmail($form->get('alternateEmail')->getData()); - } else { - $alternateEmail->addError(new FormError('L\'adresse e-mail n\'est pas valide.')); - } - - $telephone = $form->get('telephone')->getData(); - $regexTelephone = '/^[0-9\+\s\.\-\(\)]+$/'; - if(isset($telephone) && preg_match($regexTelephone, $telephone)) { - $user->setTelephone($telephone); - } else { - $telephone->addError(new FormError('Le numéro de téléphone n\'est pas valide.')); - } - - # Sauvegarde en base de données + // Sauvegarde en base de données $entityManager->flush(); - - # Message de confirmation et rechargement de la page + // Message de confirmation et rechargement de la page $this->addFlash('success', 'Votre profil a été mis à jour avec succès !'); - + // Redirection de l'utilisateur return $this->redirectToRoute('app_user'); } - - # Affichage de la page + // Affichage de la page return $this->render('user/index.html.twig', [ 'form' => $form->createView(), - 'userData' => $user, # TODO : Mettre $userData quand connexion avec API OK + 'userData' => $user, + 'isEditMode' => $isEditMode, ]); } @@ -124,31 +111,30 @@ class UserController extends AbstractController EntityManagerInterface $entityManager ): Response { - # Création du formulaire + // Création du formulaire $form = $this->createForm(ChangePasswordType::class); - # Liaison du formulaire à la requête HTTP + // Liaison du formulaire à la requête HTTP $form->handleRequest($request); - # Vérification du formulaire, s'il est bien soumis et valide + // Vérification du formulaire, s'il est bien soumis et valide if ($form->isSubmitted() && $form->isValid()) { - # Récupération des données du formulaire + // Récupération des données du formulaire $user = $this->getUser(); $plainOldPassword = $form->get('oldPassword')->getData(); $newPassword = $form->get('newPassword')->getData(); - # Vérification de l'ancien mot de passe + // Vérification de l'ancien mot de passe if (!$hasher->isPasswordValid($user, $plainOldPassword)) { $form->get('oldPassword')->addError(new FormError('L\'ancien mot de passe est incorrect.')); } else { - # Si tout est OK : Hachage du mot de passe + // Si tout est OK : Hachage du mot de passe $hashedPassword = $hasher->hashPassword($user, $newPassword); $user->setPassword($hashedPassword); - # Sauvegarde en BDD + // Sauvegarde en BDD $entityManager->flush(); - - # Message de succès pour l'utilisateur + // Message de succès pour l'utilisateur $this->addFlash('success', 'Votre mot de passe a bien été mis à jour !'); return $this->redirectToRoute('app_user_edit_password'); diff --git a/src/Entity/User.php b/src/Entity/User.php index 88e6bf1..e381c84 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -3,7 +3,6 @@ namespace App\Entity; use App\Repository\UserRepository; -use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; @@ -69,9 +68,8 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface #[ORM\Column(length: 255, name: 'first_name')] private ?string $firstName = null; - // TODO: Modifier "photo" par "image" - #[ORM\Column(length: 255, nullable: true, name: 'photo')] - private ?string $photo = null; + #[ORM\Column(length: 255, nullable: true, name: 'image')] + private ?string $image = null; #[ORM\Column(length: 20, nullable: true, name: 'telephone')] private ?string $telephone = null; @@ -290,14 +288,14 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface return $this; } - public function getPhoto(): ?string + public function getImage(): ?string { - return $this->photo; + return $this->image; } - public function setPhoto(?string $photo): static + public function setImage(?string $image): static { - $this->photo = $photo; + $this->image = $image; return $this; } @@ -314,17 +312,27 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface return $this; } + // Fonction qui permet d'afficher les données de l'API sur la page de profil public function updateFromKazUser($kazUser) : User { + // Récupération et conversion des données de l'API pour les afficher $this->setEmail($kazUser['mail']); - // Création du firstname et lastname + // Création du firstname et lastname (une seule donnée sur l'API) $name = explode(' ', $kazUser['sn']); $this->setFirstName($name[0]); // Récupération des valeurs du tableau moins la première $aLastname = array_slice($name, 1); $this->setLastName(implode(' ', $aLastname)); - - //TODO: Ajouter les champs manquants de l'objet User dans l'api kaz. + // Récupération du mail de secours + $this->setAlternateEmail($kazUser['mailDeSecours']); + $this->setEmailQuota($kazUser['mailQuota']); + $this->setHasAgoraAccess($kazUser['agoraEnabled']); + $this->setHasMobilizon($kazUser['mobilizonEnabled']); + $this->setHasNextcloudAccess($kazUser['nextcloudEnabled']); + $this->setNextcloudQuota($kazUser['nextcloudQuota']); + $this->setQuota($kazUser['quota']); + $this->setIdentifiantKaz($kazUser['identifiantKaz']); + $this->setTelephone($kazUser['telephone'] ?? null); return $this; } diff --git a/src/Form/UserProfileType.php b/src/Form/UserProfileType.php index 566ca32..89eb761 100644 --- a/src/Form/UserProfileType.php +++ b/src/Form/UserProfileType.php @@ -3,11 +3,12 @@ namespace App\Form; use App\Entity\User; -use Symfony\Component\Form\Extension\Core\Type\TelType; -use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\EmailType; use Symfony\Component\Form\Extension\Core\Type\FileType; +use Symfony\Component\Form\Extension\Core\Type\TelType; +use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Validator\Constraints\Image; @@ -27,22 +28,53 @@ class UserProfileType extends AbstractType 'label' => 'Nom', 'disabled' => true, ]) + ->add('identifiantKaz', TextType::class, [ + 'label' => 'Identifiant KAZ : ', + 'disabled' => true, + ]) ->add('email', EmailType::class, [ 'label' => 'E-mail', 'disabled' => true, ]) - ->add('alternateEmail', EmailType::class, ['label' => 'E-mail de secours']) + ->add('alternateEmail', EmailType::class, [ + 'label' => 'E-mail de secours', + 'constraints' => [ + new Regex( + pattern: '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/', + message: 'L\'adresse e-mail n\'est pas valide.' + ) + ] + ]) + ->add('emailQuota', TextType::class, [ + 'label' => 'Espace de stockage de votre boîte mail : ', + 'disabled' => true, + ]) + ->add('hasNextcloudAccess', CheckboxType::class, [ + 'label' => 'Accès au Nextcloud : ', + 'disabled' => true, + ]) + ->add('nextcloudQuota', TextType::class, [ + 'label' => 'Espace de stockage de votre Nextcloud : ', + 'disabled' => true, + ]) + ->add('hasMobilizon', CheckboxType::class, [ + 'label' => 'Accès à Mobilizon : ', + 'disabled' => true, + ]) + ->add('hasAgoraAccess', CheckboxType::class, [ + 'label' => 'Accès à l\'Agora : ', + 'disabled' => true, + ]) ->add('telephone', TelType::class, [ 'label'=>'Téléphone', 'required' => false, 'attr' => [ 'placeholder'=>'06 00 00 00 00', - 'class'=> 'w-full px-4 py-3 border border-gris-clair rounded-lg focus:outline-none focus:border-bouton focus:ring-1 focus:ring-bouton transition-shadow' ], 'constraints' => [ new Regex( pattern: '/^[0-9\+\s\.\-\(\)]+$/', - message: 'Le numéro de téléphone contient des caractères non valides' + message: 'Le numéro de téléphone n\'est pas valide.' ), new Length( max: 20, @@ -51,7 +83,7 @@ class UserProfileType extends AbstractType ], ]) ->add('image', FileType::class, [ - 'label' => 'Ma photo de profil', + 'label' => 'Mon image de profil', 'mapped' => false, 'required' => false, 'constraints' => [ diff --git a/src/Service/FileUploader.php b/src/Service/FileUploader.php index 627af1d..144216f 100644 --- a/src/Service/FileUploader.php +++ b/src/Service/FileUploader.php @@ -2,45 +2,76 @@ namespace App\Service; +use RuntimeException; +use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\HttpFoundation\File\Exception\FileException; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\String\Slugger\SluggerInterface; +/** + * Service de gestion des envois et suppressions de fichiers. + */ class FileUploader { - // On utilise la promotion de constructeur (PHP 8) : ultra moderne et concis + /** + * @param string $targetDirectory Le chemin absolu vers le dossier de dépôt. + * @param SluggerInterface $slugger Le service de nettoyage des chaînes de caractères. + */ public function __construct( - private string $targetDirectory, - private SluggerInterface $slugger, - ) { + #[Autowire('%kernel.project_dir%/public/uploads/img')] + private readonly string $targetDirectory, + private readonly SluggerInterface $slugger, + ) + { } + /** + * Traite, sécurise et déplace un fichier déposé. + * + * @param UploadedFile $file Le fichier physique à déposer. + * @return string Le nom final sécurisé et unique du fichier. + * @throws RuntimeException En cas d'échec de l'écriture sur le disque. + */ public function upload(UploadedFile $file): string { $originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME); $safeFilename = $this->slugger->slug($originalFilename); - $fileName = $safeFilename . '-' . uniqid() . '.' . $file->guessExtension(); + + // Utilisation de uniqid('', true) pour garantir une unicité absolue en production + $fileName = sprintf('%s-%s.%s', $safeFilename, uniqid('', true), $file->guessExtension()); try { $file->move($this->getTargetDirectory(), $fileName); } catch (FileException $e) { - // Ici tu peux logguer l'erreur si besoin - throw new \Exception('Erreur lors du transfert de l\'image : ' . $e->getMessage()); + throw new RuntimeException('Erreur lors du transfert de l\'image : ' . $e->getMessage(), 0, $e); } return $fileName; } + /** + * Supprime physiquement un fichier du serveur. + * + * @param string|null $fileName Le nom du fichier à supprimer. + */ public function delete(?string $fileName): void { - if ($fileName) { - $filePath = $this->getTargetDirectory() . 'FileUploader.php/' . $fileName; - if (file_exists($filePath)) { - unlink($filePath); - } + if (null === $fileName) { + return; + } + + $filePath = rtrim($this->getTargetDirectory(), '/') . '/' . $fileName; + + if (file_exists($filePath)) { + unlink($filePath); } } + /** + * Retourne le chemin du répertoire de dépôt. + * + * @return string + */ public function getTargetDirectory(): string { return $this->targetDirectory; diff --git a/src/Service/KazApiService.php b/src/Service/KazApiService.php index e754325..9725f83 100644 --- a/src/Service/KazApiService.php +++ b/src/Service/KazApiService.php @@ -50,7 +50,7 @@ class KazApiService } $data = $response->toArray(); - $this->token = $data['access_token']; // Ajustez la clé selon le format de votre API + $this->token = $data['access_token']; return $this->token; } diff --git a/templates/home/home.html.twig b/templates/home/home.html.twig index b37ef47..8b42895 100644 --- a/templates/home/home.html.twig +++ b/templates/home/home.html.twig @@ -16,8 +16,8 @@ Bienvenue sur ton espace kaznaute {{ app.user ? app.user.userIdentifier : 'visiteur' }} ! - {# Zone réservée pour les futures données de l'API - TODO : Gérer les données de l'API #} + {# Zone réservée pour les futures données de l'API et Pahéko + TODO : Gérer les données avec Pahéko, mise en service en cours par un des développeurs de l'association. Cela sera vu à posteriori du stage #}

Votre abonnement actuellement : diff --git a/templates/user/index.html.twig b/templates/user/index.html.twig index aacef5f..f2f6632 100644 --- a/templates/user/index.html.twig +++ b/templates/user/index.html.twig @@ -1,135 +1,165 @@ {% extends 'base.html.twig' %} -{% block title %}Accueil | {{ parent() }}{% endblock %} +{% block title %}Ma page de profil | {{ parent() }}{% endblock %} {% block body %}
- + {# Affichage du formulaire (seulement en mode édition) #} + {% if isEditMode %} {{ form_start(form, {'attr': {'class': 'max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 grid md:grid-cols-3 gap-8'}}) }} + {% else %} +
+ {% endif %} - {# TODO: voir si c'est pertinent avec l'API et s'il y a l'utilité d'une photo de profil #} - {# Gestion de la colone avec le choix de la photo de profil #} -
+ {# Colonne Photo #} +
+
+
+ {% if userData.image %} + Photo de profil + {% else %} +
👤 +
+ {% endif %} +
+
- {# Affichage de la photo de profil #} -
-
- {% if userData.photo %} - Photo de profil - {% else %} -
- 👤 +

Ma photo

+ + {# Affichage du champs "choisir en fichier" (seulement en mode édition) #} + {% if isEditMode %} +
+ {{ form_label(form.image, 'Choisir un fichier', {'label_attr': {'class': 'block mb-2.5 text-sm font-medium text-gris-fonce'}}) }} + {{ form_widget(form.image, { + 'attr': {'class': 'cursor-pointer bg-white border border-gris-clair text-text text-sm rounded-lg focus:outline-none focus:ring-1 focus:ring-bouton focus:border-bouton block w-full shadow-sm placeholder-gris-moyen file:mr-4 file:py-2.5 file:px-4 file:border-0 file:border-r file:border-gris-clair file:bg-gris-clair file:text-gris-fonce hover:file:bg-gris-moyen transition-colors'} + }) }} +

JPG, JPEG ou PNG (Taille max : 2Mo).

+
{{ form_errors(form.image) }}
+
+ {% endif %} +
+ + {# Colonne Infos Persos #} +
+ + {# Gestion des boutons d'action (Modifier / Annuler) et des titres #} +
+

Mon profil

+ +
+ {% if isEditMode %} + + ❌ Annuler + + {% else %} + + ✏️ Modifier + + {% endif %} +
+
+ +

Mes informations personnelles

+ +
+ {# Identifiant #} +
+ + {% if isEditMode %} + {{ form_widget(form.identifiantKaz, {'attr': {'class': 'w-full px-4 py-3 border border-gris-clair rounded-lg focus:outline-none focus:border-bouton focus:ring-1 focus:ring-bouton placeholder-gris-moyen transition-shadow'}}) }} +
{{ form_errors(form.identifiantKaz) }}
+ {% else %} +
{{ userData.identifiantKaz ?? 'Non renseigné' }}
+ {% endif %} +
+ + {# --- NOM et Prénom --- #} +
+ {# NOM #} +
+ + {% if isEditMode %} + {{ form_widget(form.lastName, {'attr': {'class': 'w-full px-4 py-3 border border-gris-clair rounded-lg focus:outline-none focus:border-bouton focus:ring-1 focus:ring-bouton placeholder-gris-moyen transition-shadow'}}) }} +
{{ form_errors(form.lastName) }}
+ {% else %} +
{{ userData.lastName }}
+ {% endif %} +
+ + {# Prénom #} +
+ + {% if isEditMode %} + {{ form_widget(form.firstName, {'attr': {'class': 'w-full px-4 py-3 border border-gris-clair rounded-lg focus:outline-none focus:border-bouton focus:ring-1 focus:ring-bouton placeholder-gris-moyen transition-shadow'}}) }} +
{{ form_errors(form.firstName) }}
+ {% else %} +
{{ userData.firstName }}
+ {% endif %} +
+
+ + {# Téléphone #} +
+ + {% if isEditMode %} + {{ form_widget(form.telephone, {'attr': {'class': 'w-full px-4 py-3 border border-gris-clair rounded-lg focus:outline-none focus:border-bouton focus:ring-1 focus:ring-bouton placeholder-gris-moyen transition-shadow'}}) }} +
{{ form_errors(form.telephone) }}
+ {% else %} +
{{ userData.telephone ?? 'Non renseigné' }}
+ {% endif %} +
+ + {# E-mail #} +
+ + {% if isEditMode %} + {{ form_widget(form.email, {'attr': {'class': 'w-full px-4 py-3 border border-gris-clair rounded-lg focus:outline-none focus:border-bouton focus:ring-1 focus:ring-bouton placeholder-gris-moyen transition-shadow'}}) }} +
{{ form_errors(form.email) }}
+ {% else %} +
{{ userData.email }}
+ {% endif %} +
+ + {# E-mail de secours #} +
+ + {% if isEditMode %} + {{ form_widget(form.alternateEmail, {'attr': {'class': 'w-full px-4 py-3 border border-gris-clair rounded-lg focus:outline-none focus:border-bouton focus:ring-1 focus:ring-bouton placeholder-gris-moyen transition-shadow'}}) }} +
{{ form_errors(form.alternateEmail) }}
+ {% else %} +
{{ userData.alternateEmail ?? 'Non renseigné' }}
+ {% endif %} +
+ + {# Affichage du bouton "Valider" (seulement en mode édition) #} + {% if isEditMode %} +
+
{% endif %}
-

Ma photo

- - {# Gestion du dépôt d'un fichier image #} -
- {{ form_label(form.image, 'Choisir un fichier', { - 'label_attr': {'class': 'block mb-2.5 text-sm font-medium text-gris-fonce'} - }) }} - {{ form_widget(form.image, { - 'attr': { - 'class': 'cursor-pointer bg-white border border-gris-clair text-text text-sm rounded-lg focus:outline-none focus:ring-1 focus:ring-bouton focus:border-bouton block w-full shadow-sm placeholder-gris-moyen file:mr-4 file:py-2.5 file:px-4 file:border-0 file:border-r file:border-gris-clair file:bg-gris-clair file:text-gris-fonce hover:file:bg-gris-moyen file:cursor-pointer transition-colors', - 'aria-describedby': 'file_input_help' - } - }) }} -

- JPG, JPEG ou PNG (Taille max : 2Mo). -

-
+ {# Fermuture du formulaire (seulement en mode édition) #} + {% if isEditMode %} + {{ form_end(form) }} + {% else %}
- - {# Gestion de la colonne avec les "infos persos" #} -
-

Mon profil

-

Mes informations personnelles

- - {# Gestion du formulaire qui regroupe toutes les infos perso #} -
- - {# Champ NOM et Prénom #} -
-
- {{ form_label(form.firstName, 'NOM :', { - 'label_attr': {'class': 'block text-sm font-semibold text-text'} - }) }} - {{ form_widget(form.firstName, { - 'attr': {'class': 'w-full px-4 py-3 border border-gris-clair rounded-lg focus:outline-none focus:border-bouton focus:ring-1 focus:ring-bouton placeholder-gris-moyen transition-shadow'} - }) }} - {# Implémentation d'un message d'errer en cas de problème #} -
- {{ form_errors(form.firstName) }} -
-
- -
- {{ form_label(form.lastName, 'Prénom :', { - 'label_attr': {'class': 'block text-sm font-semibold text-text'} - }) }} - {{ form_widget(form.lastName, { - 'attr': {'class': 'w-full px-4 py-3 border border-gris-clair rounded-lg focus:outline-none focus:border-bouton focus:ring-1 focus:ring-bouton placeholder-gris-moyen transition-shadow'} - }) }} - {# Implémentation d'un message d'errer en cas de problème #} -
- {{ form_errors(form.lastName) }} -
-
-
- - {# Champ Téléphone #} -
- {{ form_label(form.telephone, 'Numéro de téléphone', { - 'label_attr': {'class': 'block text-sm font-semibold text-text'} - }) }} - {{ form_widget(form.telephone) }} - {# Implémentation d'un message d'errer en cas de problème #} -
- {{ form_errors(form.telephone) }} -
-
- - {# Champ E-mail #} -
- {{ form_label(form.email, 'E-mail :', { - 'label_attr': {'class': 'block text-sm font-semibold text-text'} - }) }} - {{ form_widget(form.email, { - 'attr': {'class': 'w-full px-4 py-3 border border-gris-clair rounded-lg focus:outline-none focus:border-bouton focus:ring-1 focus:ring-bouton placeholder-gris-moyen transition-shadow'} - }) }} - {# Implémentation d'un message d'errer en cas de problème #} -
- {{ form_errors(form.email) }} -
-
- - {# Champ E-mail de secours #} -
- {{ form_label(form.alternateEmail, 'E-mail de secours :', { - 'label_attr': {'class': 'block text-sm font-semibold text-text'} - }) }} - {{ form_widget(form.alternateEmail, { - 'attr': {'class': 'w-full px-4 py-3 border border-gris-clair rounded-lg focus:outline-none focus:border-bouton focus:ring-1 focus:ring-bouton placeholder-gris-moyen transition-shadow'} - }) }} - {# Implémentation d'un message d'errer en cas de problème #} -
- {{ form_errors(form.alternateEmail) }} -
-
- -
- -
-
-
- {{ form_end(form) }} + {% endif %}
{% endblock %} diff --git a/templates/user/profil_infos.html.twig b/templates/user/profil_infos.html.twig index cc4c326..509b430 100644 --- a/templates/user/profil_infos.html.twig +++ b/templates/user/profil_infos.html.twig @@ -1,6 +1,6 @@ {% extends 'base.html.twig' %} -{% block title %}Accueil | {{ parent() }}{% endblock %} +{% block title %}Ma page de profil | {{ parent() }}{% endblock %} {% block body %}