diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..a55e7a1
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 5cffd8c..b935f4c 100644
--- a/README.md
+++ b/README.md
@@ -93,4 +93,9 @@ Pas de commandes.
### Question 14
```bash
symfony console doctrine:fixtures:load
+```
+
+### Question 15
+```bash
+symfony console doctrine:fixtures:load
```
\ No newline at end of file
diff --git a/config/packages/security.yaml b/config/packages/security.yaml
index 8196f0a..5beaf40 100644
--- a/config/packages/security.yaml
+++ b/config/packages/security.yaml
@@ -10,7 +10,8 @@ security:
class: App\Entity\User
property: email
role_hierarchy:
- ROLE_INSTRUCTOR: ROLE_USER
+ ROLE_ADMINISTRATEUR: [ ROLE_INSTRUCTEUR, ROLE_APPRENTI, ROLE_USER ]
+ ROLE_INSTRUCTEUR: ROLE_USER
ROLE_APPRENTI: ROLE_USER
firewalls:
dev:
@@ -34,8 +35,8 @@ security:
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- # - { path: ^/admin, roles: ROLE_ADMIN }
- # - { path: ^/profile, roles: ROLE_USER }
+ # - { path: ^/admin, roles: ROLE_ADMIN }
+ # - { path: ^/profile, roles: ROLE_USER }
when@test:
security:
diff --git a/src/Controller/AtelierController.php b/src/Controller/AtelierController.php
index 0c99ca8..bb860ce 100644
--- a/src/Controller/AtelierController.php
+++ b/src/Controller/AtelierController.php
@@ -24,7 +24,7 @@ class AtelierController extends AbstractController
]);
}
- #[IsGranted('ROLE_INSTRUCTOR')]
+ #[IsGranted('ROLE_INSTRUCTEUR')]
#[Route('/byme', name: 'app_atelier_by_me', methods: ['GET'])]
public function index_created_by_user(AtelierRepository $atelierRepository, MarkdownAtelier $markdown): Response
{
@@ -44,7 +44,7 @@ class AtelierController extends AbstractController
]);
}
- #[IsGranted('ROLE_INSTRUCTOR')]
+ #[IsGranted('ROLE_INSTRUCTEUR')]
#[Route('/new', name: 'app_atelier_new', methods: ['GET', 'POST'])]
public function new(Request $request, AtelierRepository $atelierRepository): Response
{
@@ -74,7 +74,7 @@ class AtelierController extends AbstractController
]);
}
- #[IsGranted('ROLE_INSTRUCTOR')]
+ #[IsGranted('ROLE_INSTRUCTEUR')]
#[Route('/{id}/edit', name: 'app_atelier_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, Atelier $atelier, AtelierRepository $atelierRepository): Response
{
@@ -97,7 +97,7 @@ class AtelierController extends AbstractController
]);
}
- #[IsGranted('ROLE_INSTRUCTOR')]
+ #[IsGranted('ROLE_INSTRUCTEUR')]
#[Route('/{id}', name: 'app_atelier_delete', methods: ['POST'])]
public function delete(Request $request, Atelier $atelier, AtelierRepository $atelierRepository): Response
{
diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php
index 73da5ac..3269b24 100644
--- a/src/Controller/SecurityController.php
+++ b/src/Controller/SecurityController.php
@@ -13,7 +13,7 @@ class SecurityController extends AbstractController
public function login(AuthenticationUtils $authenticationUtils): Response
{
if ($this->getUser()) {
- return $this->redirectToRoute('app_atelier_index');
+ return $this->redirectToRoute('app_index');
}
// get the login error if there is one
diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php
new file mode 100644
index 0000000..6e6bad2
--- /dev/null
+++ b/src/Controller/UserController.php
@@ -0,0 +1,88 @@
+render('user/index.html.twig', [
+ 'users' => $userRepository->findAll(),
+ ]);
+ }
+
+ #[Route('/new', name: 'app_user_new', methods: ['GET', 'POST'])]
+ public function new(Request $request, UserRepository $userRepository, UserPasswordHasherInterface $userPasswordHasher): Response
+ {
+ $user = new User();
+ $form = $this->createForm(NewUserType::class, $user);
+ $form->handleRequest($request);
+
+ if ($form->isSubmitted() && $form->isValid()) {
+ $user->setPassword(
+ $userPasswordHasher->hashPassword(
+ $user,
+ $form->get('plainPassword')->getData()
+ )
+ );
+ $userRepository->save($user, true);
+
+ return $this->redirectToRoute('app_user_index', [], Response::HTTP_SEE_OTHER);
+ }
+
+ return $this->renderForm('user/new.html.twig', [
+ 'user' => $user,
+ 'form' => $form,
+ ]);
+ }
+
+ #[Route('/{id}', name: 'app_user_show', methods: ['GET'])]
+ public function show(User $user): Response
+ {
+ return $this->render('user/show.html.twig', [
+ 'user' => $user,
+ ]);
+ }
+
+ #[Route('/{id}/edit', name: 'app_user_edit', methods: ['GET', 'POST'])]
+ public function edit(Request $request, User $user, UserRepository $userRepository): Response
+ {
+ $form = $this->createForm(UserType::class, $user);
+ $form->handleRequest($request);
+
+ if ($form->isSubmitted() && $form->isValid()) {
+ $userRepository->save($user, true);
+
+ return $this->redirectToRoute('app_user_index', [], Response::HTTP_SEE_OTHER);
+ }
+
+ return $this->renderForm('user/edit.html.twig', [
+ 'user' => $user,
+ 'form' => $form,
+ ]);
+ }
+
+ #[Route('/{id}', name: 'app_user_delete', methods: ['POST'])]
+ public function delete(Request $request, User $user, UserRepository $userRepository): Response
+ {
+ if ($this->isCsrfTokenValid('delete' . $user->getId(), $request->request->get('_token'))) {
+ $userRepository->remove($user, true);
+ }
+
+ return $this->redirectToRoute('app_user_index', [], Response::HTTP_SEE_OTHER);
+ }
+}
diff --git a/src/DataFixtures/AtelierFixture.php b/src/DataFixtures/AtelierFixture.php
index be7dc3c..8606d61 100644
--- a/src/DataFixtures/AtelierFixture.php
+++ b/src/DataFixtures/AtelierFixture.php
@@ -7,29 +7,45 @@ use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
use Faker\Generator;
+use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
class AtelierFixture extends Fixture
{
+ private UserPasswordHasherInterface $userPasswordHasher;
+
+ public function __construct(UserPasswordHasherInterface $userPasswordHasher)
+ {
+ $this->userPasswordHasher = $userPasswordHasher;
+ }
+
public function load(ObjectManager $manager): void
{
$faker = \Faker\Factory::create("fr_FR");
$user = new User();
- $user->setEmail('test@hotmail.com')
- ->setNom("test")
- ->setPrenom("test")
- ->setPassword("");
+ $user
+ ->setEmail('admin@admin.fr')
+ ->setNom("admin")
+ ->setPrenom("admin")
+ ->setRoles(['ROLE_ADMINISTRATEUR']);
+
+ $user->setPassword(
+ $this->userPasswordHasher->hashPassword(
+ $user,
+ 'admin'
+ )
+ );
+ $manager->persist($user);
$users = $this->createUsers($manager, $faker);
- $manager->persist($user);
for ($i = 0; $i <= 20; $i++) {
$atelier = new Atelier();
$atelier->setNom($faker->word)
->setDescription("# " . $faker->sentence(3) . "\n" . $faker->paragraph())
->setInstructeur($user);
- foreach ($faker->randomElements($users, $faker->randomNumber() % sizeof($users)) as $user) {
- $atelier->addEleve($user);
+ foreach ($faker->randomElements($users, $faker->randomNumber() % sizeof($users)) as $eleve) {
+ $atelier->addEleve($eleve);
}
$manager->persist($atelier);
diff --git a/src/Entity/User.php b/src/Entity/User.php
index e56617c..9db0d01 100644
--- a/src/Entity/User.php
+++ b/src/Entity/User.php
@@ -83,9 +83,9 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
- $roles[] = 'ROLE_USER';
- $roles[] = 'ROLE_INSTRUCTOR';
- $roles[] = 'ROLE_APPRENTI';
+ if (sizeof($roles) == 0) {
+ $roles[] = "ROLE_APPRENTI";
+ }
return array_unique($roles);
}
diff --git a/src/Form/NewUserType.php b/src/Form/NewUserType.php
new file mode 100644
index 0000000..2940762
--- /dev/null
+++ b/src/Form/NewUserType.php
@@ -0,0 +1,71 @@
+add('email')
+ ->add('plainPassword', PasswordType::class, [
+ // instead of being set onto the object directly,
+ // this is read and encoded in the controller
+ 'mapped' => false,
+ 'attr' => ['autocomplete' => 'new-password'],
+ 'constraints' => [
+ new NotBlank([
+ 'message' => 'Please enter a password',
+ ]),
+ new Length([
+ 'min' => 6,
+ 'minMessage' => 'Your password should be at least {{ limit }} characters',
+ // max length allowed by Symfony for security reasons
+ 'max' => 4096,
+ ]),
+ ],
+ ])
+ ->add('roles', ChoiceType::class, [
+ 'choices' => [
+ 'Apprenti' => 'ROLE_APPRENTI',
+ 'Instructeur' => 'ROLE_INSTRUCTEUR',
+ 'Admin' => 'ROLE_ADMINISTRATEUR'
+ ],
+ 'required' => true,
+ 'multiple' => false,
+ 'expanded' => false,
+ ])
+ ->add('nom')
+ ->add('prenom');
+
+ $builder->get('roles')
+ ->addModelTransformer(new CallbackTransformer(
+ function ($rolesArray) {
+ // transform the array to a string
+ return count($rolesArray) ? $rolesArray[0] : null;
+ },
+ function ($rolesString) {
+ // transform the string back to an array
+ return [$rolesString];
+ }
+ ));
+
+ }
+
+ public function configureOptions(OptionsResolver $resolver): void
+ {
+ $resolver->setDefaults([
+ 'data_class' => User::class,
+ ]);
+ }
+}
diff --git a/src/Form/UserType.php b/src/Form/UserType.php
new file mode 100644
index 0000000..02f7926
--- /dev/null
+++ b/src/Form/UserType.php
@@ -0,0 +1,52 @@
+add('email')
+ ->add('roles', ChoiceType::class, [
+ 'choices' => [
+ 'Apprenti' => 'ROLE_APPRENTI',
+ 'Instructeur' => 'ROLE_INSTRUCTEUR',
+ 'Admin' => 'ROLE_ADMINISTRATEUR'
+ ],
+ 'required' => true,
+ 'multiple' => false,
+ 'expanded' => false,
+ ])
+ ->add('nom')
+ ->add('prenom');
+
+ $builder->get('roles')
+ ->addModelTransformer(new CallbackTransformer(
+ function ($rolesArray) {
+ // transform the array to a string
+ return count($rolesArray)? $rolesArray[0]: null;
+ },
+ function ($rolesString) {
+ // transform the string back to an array
+ return [$rolesString];
+ }
+ ));
+
+ }
+
+ public function configureOptions(OptionsResolver $resolver): void
+ {
+ $resolver->setDefaults([
+ 'data_class' => User::class,
+ ]);
+ }
+}
diff --git a/src/Security/AppAuthentificatorAuthenticator.php b/src/Security/AppAuthentificatorAuthenticator.php
index 946b252..7ca73ed 100644
--- a/src/Security/AppAuthentificatorAuthenticator.php
+++ b/src/Security/AppAuthentificatorAuthenticator.php
@@ -46,7 +46,7 @@ class AppAuthentificatorAuthenticator extends AbstractLoginFormAuthenticator
return new RedirectResponse($targetPath);
}
- return new RedirectResponse($this->urlGenerator->generate('app_atelier_index'));
+ return new RedirectResponse($this->urlGenerator->generate('app_index'));
}
protected function getLoginUrl(Request $request): string
diff --git a/templates/components/navbar.html.twig b/templates/components/navbar.html.twig
index a590160..31c43b8 100644
--- a/templates/components/navbar.html.twig
+++ b/templates/components/navbar.html.twig
@@ -13,16 +13,22 @@
Atelier
- {% if app.user %}
+
+ {% if app.user and is_granted('ROLE_INSTRUCTEUR') %}
Mes Ateliers
{% endif %}
- {% if app.user %}
+ {% if app.user and is_granted('ROLE_APPRENTI') %}
Mes Formations
{% endif %}
+ {% if app.user and is_granted('ROLE_ADMINISTRATEUR') %}
+
+ Liste des Utilisateurs
+
+ {% endif %}
{% if app.user %}
diff --git a/templates/user/_delete_form.html.twig b/templates/user/_delete_form.html.twig
new file mode 100644
index 0000000..6d59fa6
--- /dev/null
+++ b/templates/user/_delete_form.html.twig
@@ -0,0 +1,4 @@
+
diff --git a/templates/user/_form.html.twig b/templates/user/_form.html.twig
new file mode 100644
index 0000000..bf20b98
--- /dev/null
+++ b/templates/user/_form.html.twig
@@ -0,0 +1,4 @@
+{{ form_start(form) }}
+ {{ form_widget(form) }}
+
+{{ form_end(form) }}
diff --git a/templates/user/edit.html.twig b/templates/user/edit.html.twig
new file mode 100644
index 0000000..141d94a
--- /dev/null
+++ b/templates/user/edit.html.twig
@@ -0,0 +1,13 @@
+{% extends 'base.html.twig' %}
+
+{% block title %}Edit User{% endblock %}
+
+{% block body %}
+ Edit User
+
+ {{ include('user/_form.html.twig', {'button_label': 'Update'}) }}
+
+ back to list
+
+ {{ include('user/_delete_form.html.twig') }}
+{% endblock %}
diff --git a/templates/user/index.html.twig b/templates/user/index.html.twig
new file mode 100644
index 0000000..e2e3f7f
--- /dev/null
+++ b/templates/user/index.html.twig
@@ -0,0 +1,41 @@
+{% extends 'base.html.twig' %}
+
+{% block title %}User index{% endblock %}
+
+{% block body %}
+ User index
+
+
+
+
+ Id |
+ Email |
+ Roles |
+ Nom |
+ Prenom |
+ actions |
+
+
+
+ {% for user in users %}
+
+ {{ user.id }} |
+ {{ user.email }} |
+ {{ user.roles ? user.roles[0] : '' }} |
+ {{ user.nom }} |
+ {{ user.prenom }} |
+
+ show
+ edit
+ |
+
+ {% else %}
+
+ no records found |
+
+ {% endfor %}
+
+
+
+ Create new
+{% endblock %}
diff --git a/templates/user/new.html.twig b/templates/user/new.html.twig
new file mode 100644
index 0000000..35e728d
--- /dev/null
+++ b/templates/user/new.html.twig
@@ -0,0 +1,11 @@
+{% extends 'base.html.twig' %}
+
+{% block title %}New User{% endblock %}
+
+{% block body %}
+ Create new User
+
+ {{ include('user/_form.html.twig') }}
+
+ back to list
+{% endblock %}
diff --git a/templates/user/show.html.twig b/templates/user/show.html.twig
new file mode 100644
index 0000000..ffc5b61
--- /dev/null
+++ b/templates/user/show.html.twig
@@ -0,0 +1,38 @@
+{% extends 'base.html.twig' %}
+
+{% block title %}User{% endblock %}
+
+{% block body %}
+ User
+
+
+
+
+ Id |
+ {{ user.id }} |
+
+
+ Email |
+ {{ user.email }} |
+
+
+ Roles |
+ {{ user.roles ? user.roles[0] : '' }} |
+
+
+ Nom |
+ {{ user.nom }} |
+
+
+ Prenom |
+ {{ user.prenom }} |
+
+
+
+
+ back to list
+
+ edit
+
+ {{ include('user/_delete_form.html.twig') }}
+{% endblock %}